diff options
Diffstat (limited to '')
41 files changed, 1975 insertions, 296 deletions
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index e61748ca3..c629f9357 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -2,9 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <array> +#include "common/common_paths.h" #include "common/common_types.h" +#include "common/file_util.h" #include "common/logging/log.h" +#include "common/string_util.h" #include "common/swap.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" @@ -16,6 +20,7 @@ #include "core/hle/service/acc/profile_manager.h" namespace Service::Account { + // TODO: RE this structure struct UserData { INSERT_PADDING_WORDS(1); @@ -27,6 +32,29 @@ struct UserData { }; static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); +// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg +// used as a backup should the one on disk not exist +constexpr u32 backup_jpeg_size = 107; +constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{ + 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, + 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, + 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, + 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, + 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, + 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, +}}; + +static std::string GetImagePath(UUID uuid) { + return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; +} + +static constexpr u32 SanitizeJPEGSize(std::size_t size) { + constexpr std::size_t max_jpeg_image_size = 0x20000; + return static_cast<u32>(std::min(size, max_jpeg_image_size)); +} + class IProfile final : public ServiceFramework<IProfile> { public: explicit IProfile(UUID user_id, ProfileManager& profile_manager) @@ -73,32 +101,42 @@ private: } void LoadImage(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); - // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg - // TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000 - constexpr u32 jpeg_size = 107; - static constexpr std::array<u8, jpeg_size> jpeg{ - 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, - 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, - 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, - 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, - 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00, - 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, - 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, - }; - ctx.WriteBuffer(jpeg); + LOG_DEBUG(Service_ACC, "called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(jpeg_size); + + const FileUtil::IOFile image(GetImagePath(user_id), "rb"); + if (!image.IsOpen()) { + LOG_WARNING(Service_ACC, + "Failed to load user provided image! Falling back to built-in backup..."); + ctx.WriteBuffer(backup_jpeg); + rb.Push<u32>(backup_jpeg_size); + return; + } + + const u32 size = SanitizeJPEGSize(image.GetSize()); + std::vector<u8> buffer(size); + image.ReadBytes(buffer.data(), buffer.size()); + + ctx.WriteBuffer(buffer.data(), buffer.size()); + rb.Push<u32>(size); } void GetImageSize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); - constexpr u32 jpeg_size = 107; + LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(jpeg_size); + + const FileUtil::IOFile image(GetImagePath(user_id), "rb"); + + if (!image.IsOpen()) { + LOG_WARNING(Service_ACC, + "Failed to load user provided image! Falling back to built-in backup..."); + rb.Push<u32>(backup_jpeg_size); + } else { + rb.Push<u32>(SanitizeJPEGSize(image.GetSize())); + } } const ProfileManager& profile_manager; @@ -204,6 +242,30 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo LOG_DEBUG(Service_ACC, "called"); } +void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ACC, "called"); + // A u8 is passed into this function which we can safely ignore. It's to determine if we have + // access to use the network or not by the looks of it + IPC::ResponseBuilder rb{ctx, 6}; + if (profile_manager->GetUserCount() != 1) { + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u128>(INVALID_UUID); + return; + } + + const auto user_list = profile_manager->GetAllUsers(); + if (std::all_of(user_list.begin(), user_list.end(), + [](const auto& user) { return user.uuid == INVALID_UUID; })) { + rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code + rb.PushRaw<u128>(INVALID_UUID); + return; + } + + // Select the first user we have + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid); +} + Module::Interface::Interface(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager, const char* name) : ServiceFramework(name), module(std::move(module)), diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index c7ed74351..89b2104fa 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -27,6 +27,7 @@ public: void InitializeApplicationInfo(Kernel::HLERequestContext& ctx); void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx); void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx); + void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx); protected: std::shared_ptr<Module> module; diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index ad455c3a7..5e2030355 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -17,7 +17,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {5, &ACC_SU::GetProfile, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, - {51, nullptr, "TrySelectUserWithoutInteraction"}, + {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, {60, nullptr, "ListOpenContextStoredUsers"}, {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 72d4adf35..a4d705b45 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -17,7 +17,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {5, &ACC_U0::GetProfile, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, - {51, nullptr, "TrySelectUserWithoutInteraction"}, + {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, {60, nullptr, "ListOpenContextStoredUsers"}, {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index d480f08e5..8fffc93b5 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -17,7 +17,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {5, &ACC_U1::GetProfile, "GetProfile"}, {6, nullptr, "GetProfileDigest"}, {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, - {51, nullptr, "TrySelectUserWithoutInteraction"}, + {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, {60, nullptr, "ListOpenContextStoredUsers"}, {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index bcb3475db..968263846 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -2,42 +2,83 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> #include <random> -#include <boost/optional.hpp> + +#include <fmt/format.h> + +#include "common/file_util.h" #include "core/hle/service/acc/profile_manager.h" #include "core/settings.h" namespace Service::Account { + +struct UserRaw { + UUID uuid; + UUID uuid2; + u64 timestamp; + ProfileUsername username; + INSERT_PADDING_BYTES(0x80); +}; +static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size."); + +struct ProfileDataRaw { + INSERT_PADDING_BYTES(0x10); + std::array<UserRaw, MAX_USERS> users; +}; +static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size."); + // TODO(ogniK): Get actual error codes constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1); constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2); constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); -const UUID& UUID::Generate() { +constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/"; + +UUID UUID::Generate() { std::random_device device; std::mt19937 gen(device()); std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); - uuid[0] = distribution(gen); - uuid[1] = distribution(gen); - return *this; + return UUID{distribution(gen), distribution(gen)}; +} + +std::string UUID::Format() const { + return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); +} + +std::string UUID::FormatSwitch() const { + std::array<u8, 16> s{}; + std::memcpy(s.data(), uuid.data(), sizeof(u128)); + return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{" + ":02x}{:02x}{:02x}{:02x}{:02x}", + s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], + s[12], s[13], s[14], s[15]); } ProfileManager::ProfileManager() { - // TODO(ogniK): Create the default user we have for now until loading/saving users is added - auto user_uuid = UUID{1, 0}; - ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess()); - OpenUser(user_uuid); + ParseUserSaveFile(); + + if (user_count == 0) + CreateNewUser(UUID::Generate(), "yuzu"); + + auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1); + if (UserExistsIndex(current)) + current = 0; + + OpenUser(*GetUser(current)); } -ProfileManager::~ProfileManager() = default; +ProfileManager::~ProfileManager() { + WriteUserSaveFile(); +} /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the /// internal management of the users profiles -boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { +std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) { if (user_count >= MAX_USERS) { - return boost::none; + return {}; } - profiles[user_count] = user; + profiles[user_count] = profile; return user_count++; } @@ -56,7 +97,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) { /// Helper function to register a user to the system ResultCode ProfileManager::AddUser(const ProfileInfo& user) { - if (AddToProfiles(user) == boost::none) { + if (!AddToProfiles(user)) { return ERROR_TOO_MANY_USERS; } return RESULT_SUCCESS; @@ -101,31 +142,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) return CreateNewUser(uuid, username_output); } +std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { + if (index >= MAX_USERS) { + return {}; + } + + return profiles[index].user_uuid; +} + /// Returns a users profile index based on their user id. -boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { +std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { if (!uuid) { - return boost::none; + return {}; } - auto iter = std::find_if(profiles.begin(), profiles.end(), - [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); + + const auto iter = std::find_if(profiles.begin(), profiles.end(), + [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); if (iter == profiles.end()) { - return boost::none; + return {}; } + return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); } /// Returns a users profile index based on their profile -boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { +std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { return GetUserIndex(user.user_uuid); } /// Returns the data structure used by the switch when GetProfileBase is called on acc:* -bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, - ProfileBase& profile) const { - if (index == boost::none || index >= MAX_USERS) { +bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const { + if (!index || index >= MAX_USERS) { return false; } - const auto& prof_info = profiles[index.get()]; + const auto& prof_info = profiles[*index]; profile.user_uuid = prof_info.user_uuid; profile.username = prof_info.username; profile.timestamp = prof_info.creation_time; @@ -134,7 +184,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, /// Returns the data structure used by the switch when GetProfileBase is called on acc:* bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const { - auto idx = GetUserIndex(uuid); + const auto idx = GetUserIndex(uuid); return GetProfileBase(idx, profile); } @@ -161,26 +211,34 @@ std::size_t ProfileManager::GetOpenUserCount() const { /// Checks if a user id exists in our profile manager bool ProfileManager::UserExists(UUID uuid) const { - return (GetUserIndex(uuid) != boost::none); + return GetUserIndex(uuid).has_value(); +} + +bool ProfileManager::UserExistsIndex(std::size_t index) const { + if (index >= MAX_USERS) + return false; + return profiles[index].user_uuid.uuid != INVALID_UUID; } /// Opens a specific user void ProfileManager::OpenUser(UUID uuid) { - auto idx = GetUserIndex(uuid); - if (idx == boost::none) { + const auto idx = GetUserIndex(uuid); + if (!idx) { return; } - profiles[idx.get()].is_open = true; + + profiles[*idx].is_open = true; last_opened_user = uuid; } /// Closes a specific user void ProfileManager::CloseUser(UUID uuid) { - auto idx = GetUserIndex(uuid); - if (idx == boost::none) { + const auto idx = GetUserIndex(uuid); + if (!idx) { return; } - profiles[idx.get()].is_open = false; + + profiles[*idx].is_open = false; } /// Gets all valid user ids on the system @@ -210,10 +268,10 @@ UUID ProfileManager::GetLastOpenedUser() const { } /// Return the users profile base and the unknown arbitary data. -bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, +bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, ProfileData& data) const { if (GetProfileBase(index, profile)) { - data = profiles[index.get()].data; + data = profiles[*index].data; return true; } return false; @@ -222,7 +280,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P /// Return the users profile base and the unknown arbitary data. bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const { - auto idx = GetUserIndex(uuid); + const auto idx = GetUserIndex(uuid); return GetProfileBaseAndData(idx, profile, data); } @@ -239,4 +297,97 @@ bool ProfileManager::CanSystemRegisterUser() const { // emulate qlaunch. Update this to dynamically change. } +bool ProfileManager::RemoveUser(UUID uuid) { + const auto index = GetUserIndex(uuid); + if (!index) { + return false; + } + + profiles[*index] = ProfileInfo{}; + std::stable_partition(profiles.begin(), profiles.end(), + [](const ProfileInfo& profile) { return profile.user_uuid; }); + return true; +} + +bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { + const auto index = GetUserIndex(uuid); + if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) { + return false; + } + + auto& profile = profiles[*index]; + profile.user_uuid = profile_new.user_uuid; + profile.username = profile_new.username; + profile.creation_time = profile_new.timestamp; + + return true; +} + +void ProfileManager::ParseUserSaveFile() { + FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", + "rb"); + + if (!save.IsOpen()) { + LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " + "user 'yuzu' with random UUID."); + return; + } + + ProfileDataRaw data; + if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) { + LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user " + "'yuzu' with random UUID."); + return; + } + + for (const auto& user : data.users) { + if (user.uuid == UUID(INVALID_UUID)) { + continue; + } + + AddUser({user.uuid, user.username, user.timestamp, {}, false}); + } + + std::stable_partition(profiles.begin(), profiles.end(), + [](const ProfileInfo& profile) { return profile.user_uuid; }); +} + +void ProfileManager::WriteUserSaveFile() { + ProfileDataRaw raw{}; + + for (std::size_t i = 0; i < MAX_USERS; ++i) { + raw.users[i].username = profiles[i].username; + raw.users[i].uuid2 = profiles[i].user_uuid; + raw.users[i].uuid = profiles[i].user_uuid; + raw.users[i].timestamp = profiles[i].creation_time; + } + + const auto raw_path = + FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; + if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) + FileUtil::Delete(raw_path); + + const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat"; + + if (!FileUtil::CreateFullPath(path)) { + LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory " + "nand/system/save/8000000000000010/su/avators to mitigate this " + "issue."); + return; + } + + FileUtil::IOFile save(path, "wb"); + + if (!save.IsOpen()) { + LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " + "made in current session will be saved."); + return; + } + + save.Resize(sizeof(ProfileDataRaw)); + save.WriteBytes(&raw, sizeof(ProfileDataRaw)); +} + }; // namespace Service::Account diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index bffd4cf4d..d2d8e6c6b 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -5,8 +5,8 @@ #pragma once #include <array> +#include <optional> -#include "boost/optional.hpp" #include "common/common_types.h" #include "common/swap.h" #include "core/hle/result.h" @@ -36,19 +36,20 @@ struct UUID { } // TODO(ogniK): Properly generate uuids based on RFC-4122 - const UUID& Generate(); + static UUID Generate(); // Set the UUID to {0,0} to be considered an invalid user void Invalidate() { uuid = INVALID_UUID; } - std::string Format() const { - return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); - } + + std::string Format() const; + std::string FormatSwitch() const; }; static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); -using ProfileUsername = std::array<u8, 0x20>; +constexpr std::size_t profile_username_size = 32; +using ProfileUsername = std::array<u8, profile_username_size>; using ProfileData = std::array<u8, MAX_DATA>; using UserIDArray = std::array<UUID, MAX_USERS>; @@ -81,18 +82,19 @@ static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size"); /// objects class ProfileManager { public: - ProfileManager(); // TODO(ogniK): Load from system save + ProfileManager(); ~ProfileManager(); ResultCode AddUser(const ProfileInfo& user); ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); ResultCode CreateNewUser(UUID uuid, const std::string& username); - boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; - boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; - bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; + std::optional<UUID> GetUser(std::size_t index) const; + std::optional<std::size_t> GetUserIndex(const UUID& uuid) const; + std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; + bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const; bool GetProfileBase(UUID uuid, ProfileBase& profile) const; bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; - bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, + bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, ProfileData& data) const; bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, @@ -100,6 +102,7 @@ public: std::size_t GetUserCount() const; std::size_t GetOpenUserCount() const; bool UserExists(UUID uuid) const; + bool UserExistsIndex(std::size_t index) const; void OpenUser(UUID uuid); void CloseUser(UUID uuid); UserIDArray GetOpenUsers() const; @@ -108,11 +111,17 @@ public: bool CanSystemRegisterUser() const; + bool RemoveUser(UUID uuid); + bool SetProfileBase(UUID uuid, const ProfileBase& profile_new); + private: + void ParseUserSaveFile(); + void WriteUserSaveFile(); + std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); + bool RemoveProfileAtIndex(std::size_t index); + std::array<ProfileInfo, MAX_USERS> profiles{}; std::size_t user_count = 0; - boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); - bool RemoveProfileAtIndex(std::size_t index); UUID last_opened_user{INVALID_UUID}; }; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index ecf72ae24..3758ecae1 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -4,11 +4,13 @@ #include <array> #include <cinttypes> +#include <cstring> #include <stack> #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/process.h" +#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" @@ -26,6 +28,16 @@ namespace Service::AM { +constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; + +struct LaunchParameters { + u32_le magic; + u32_le is_account_selected; + u128 current_user; + INSERT_PADDING_BYTES(0x70); +}; +static_assert(sizeof(LaunchParameters) == 0x88); + IWindowController::IWindowController() : ServiceFramework("IWindowController") { // clang-format off static const FunctionInfo functions[] = { @@ -191,8 +203,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger ISelfController::~ISelfController() = default; void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { - // Takes 3 input u8s with each field located immediately after the previous u8, these are - // bool flags. No output. + // Takes 3 input u8s with each field located immediately after the previous + // u8, these are bool flags. No output. IPC::RequestParser rp{ctx}; @@ -246,8 +258,8 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont } void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { - // Takes 3 input u8s with each field located immediately after the previous u8, these are - // bool flags. No output. + // Takes 3 input u8s with each field located immediately after the previous + // u8, these are bool flags. No output. IPC::RequestParser rp{ctx}; bool enabled = rp.Pop<bool>(); @@ -290,8 +302,8 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c } void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { - // TODO(Subv): Find out how AM determines the display to use, for now just create the layer - // in the Default display. + // TODO(Subv): Find out how AM determines the display to use, for now just + // create the layer in the Default display. u64 display_id = nvflinger->OpenDisplay("Default"); u64 layer_id = nvflinger->CreateLayer(display_id); @@ -326,7 +338,54 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c LOG_WARNING(Service_AM, "(STUBBED) called"); } -ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { +AppletMessageQueue::AppletMessageQueue() { + auto& kernel = Core::System::GetInstance().Kernel(); + on_new_message = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, + "AMMessageQueue:OnMessageRecieved"); + on_operation_mode_changed = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, + "AMMessageQueue:OperationModeChanged"); +} + +AppletMessageQueue::~AppletMessageQueue() = default; + +const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetMesssageRecieveEvent() const { + return on_new_message; +} + +const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetOperationModeChangedEvent() const { + return on_operation_mode_changed; +} + +void AppletMessageQueue::PushMessage(AppletMessage msg) { + messages.push(msg); + on_new_message->Signal(); +} + +AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { + if (messages.empty()) { + on_new_message->Clear(); + return AppletMessage::NoMessage; + } + auto msg = messages.front(); + messages.pop(); + if (messages.empty()) { + on_new_message->Clear(); + } + return msg; +} + +std::size_t AppletMessageQueue::GetMessageCount() const { + return messages.size(); +} + +void AppletMessageQueue::OperationModeChanged() { + PushMessage(AppletMessage::OperationModeChanged); + PushMessage(AppletMessage::PerformanceModeChanged); + on_operation_mode_changed->Signal(); +} + +ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("ICommonStateGetter"), msg_queue(std::move(msg_queue)) { // clang-format off static const FunctionInfo functions[] = { {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, @@ -376,21 +435,19 @@ void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) { } void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) { - event->Signal(); - IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(event); + rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent()); - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "called"); } void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(15); + rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage()); - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "called"); } void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { @@ -402,13 +459,11 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { } void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) { - event->Signal(); - IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(event); + rb.PushCopyObjects(msg_queue->GetOperationModeChangedEvent()); - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "called"); } void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) { @@ -432,7 +487,7 @@ void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld)); - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "called"); } void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { @@ -442,7 +497,7 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked : APM::PerformanceMode::Handheld)); - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "called"); } class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { @@ -678,7 +733,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {70, nullptr, "RequestToShutdown"}, {71, nullptr, "RequestToReboot"}, {80, nullptr, "ExitAndRequestToShowThanksMessage"}, - {90, nullptr, "EnableApplicationCrashReport"}, + {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"}, {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"}, {101, nullptr, "SetApplicationCopyrightImage"}, {102, nullptr, "SetApplicationCopyrightVisibility"}, @@ -697,6 +752,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF IApplicationFunctions::~IApplicationFunctions() = default; +void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed( Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; @@ -724,20 +785,23 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx } void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { - constexpr std::array<u8, 0x88> data{{ - 0xca, 0x97, 0x94, 0xc7, // Magic - 1, 0, 0, 0, // IsAccountSelected (bool) - 1, 0, 0, 0, // User Id (word 0) - 0, 0, 0, 0, // User Id (word 1) - 0, 0, 0, 0, // User Id (word 2) - 0, 0, 0, 0 // User Id (word 3) - }}; + LaunchParameters params{}; + + params.magic = POP_LAUNCH_PARAMETER_MAGIC; + params.is_account_selected = 1; - std::vector<u8> buffer(data.begin(), data.end()); + Account::ProfileManager profile_manager{}; + const auto uuid = profile_manager.GetUser(Settings::values.current_user); + ASSERT(uuid); + params.current_user = uuid->uuid; IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); + + std::vector<u8> buffer(sizeof(LaunchParameters)); + std::memcpy(buffer.data(), ¶ms, buffer.size()); + rb.PushIpcInterface<AM::IStorage>(buffer); LOG_DEBUG(Service_AM, "called"); @@ -763,7 +827,8 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { // Takes an input u32 Result, no output. - // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak. + // For example, in some cases official apps use this with error 0x2A2 then + // uses svcBreak. IPC::RequestParser rp{ctx}; u32 result = rp.Pop<u32>(); @@ -825,8 +890,12 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) { void InstallInterfaces(SM::ServiceManager& service_manager, std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { - std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager); - std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager); + auto message_queue = std::make_shared<AppletMessageQueue>(); + message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on + // game boot + + std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager); + std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager); std::make_shared<IdleSys>()->InstallAsService(service_manager); std::make_shared<OMM>()->InstallAsService(service_manager); std::make_shared<SPSM>()->InstallAsService(service_manager); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 095f94851..5a3fcba8f 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -5,6 +5,7 @@ #pragma once #include <memory> +#include <queue> #include "core/hle/service/service.h" namespace Kernel { @@ -39,6 +40,31 @@ enum SystemLanguage { TraditionalChinese = 16, }; +class AppletMessageQueue { +public: + enum class AppletMessage : u32 { + NoMessage = 0, + FocusStateChanged = 15, + OperationModeChanged = 30, + PerformanceModeChanged = 31, + }; + + AppletMessageQueue(); + ~AppletMessageQueue(); + + const Kernel::SharedPtr<Kernel::Event>& GetMesssageRecieveEvent() const; + const Kernel::SharedPtr<Kernel::Event>& GetOperationModeChangedEvent() const; + void PushMessage(AppletMessage msg); + AppletMessage PopMessage(); + std::size_t GetMessageCount() const; + void OperationModeChanged(); + +private: + std::queue<AppletMessage> messages; + Kernel::SharedPtr<Kernel::Event> on_new_message; + Kernel::SharedPtr<Kernel::Event> on_operation_mode_changed; +}; + class IWindowController final : public ServiceFramework<IWindowController> { public: IWindowController(); @@ -102,7 +128,7 @@ private: class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { public: - ICommonStateGetter(); + explicit ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue); ~ICommonStateGetter() override; private: @@ -126,6 +152,7 @@ private: void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); Kernel::SharedPtr<Kernel::Event> event; + std::shared_ptr<AppletMessageQueue> msg_queue; }; class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { @@ -158,6 +185,7 @@ private: void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx); void EndBlockingHomeButton(Kernel::HLERequestContext& ctx); + void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx); }; class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 68ea778e8..ec93e3529 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -12,8 +12,10 @@ namespace Service::AM { class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { public: - explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) { + explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)), + msg_queue(std::move(msg_queue)) { static const FunctionInfo functions[] = { {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, @@ -32,7 +34,7 @@ private: void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(); + rb.PushIpcInterface<ICommonStateGetter>(msg_queue); LOG_DEBUG(Service_AM, "called"); } @@ -93,12 +95,15 @@ private: } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + std::shared_ptr<AppletMessageQueue> msg_queue; }; class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { public: - explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)) { + explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)), + msg_queue(std::move(msg_queue)) { static const FunctionInfo functions[] = { {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"}, @@ -119,7 +124,7 @@ private: void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(); + rb.PushIpcInterface<ICommonStateGetter>(msg_queue); LOG_DEBUG(Service_AM, "called"); } @@ -186,31 +191,34 @@ private: LOG_DEBUG(Service_AM, "called"); } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + std::shared_ptr<AppletMessageQueue> msg_queue; }; void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISystemAppletProxy>(nvflinger); + rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue); LOG_DEBUG(Service_AM, "called"); } void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); + rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue); LOG_DEBUG(Service_AM, "called"); } void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); + rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue); LOG_DEBUG(Service_AM, "called"); } -AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) { +AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)), + msg_queue(std::move(msg_queue)) { // clang-format off static const FunctionInfo functions[] = { {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, @@ -228,4 +236,8 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) AppletAE::~AppletAE() = default; +const std::shared_ptr<AppletMessageQueue>& AppletAE::GetMessageQueue() const { + return msg_queue; +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h index 1ed77baa4..902db2665 100644 --- a/src/core/hle/service/am/applet_ae.h +++ b/src/core/hle/service/am/applet_ae.h @@ -17,15 +17,19 @@ namespace AM { class AppletAE final : public ServiceFramework<AppletAE> { public: - explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); + explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue); ~AppletAE() override; + const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const; + private: void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx); void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx); void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx); std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + std::shared_ptr<AppletMessageQueue> msg_queue; }; } // namespace AM diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index 60717afd9..20c8d5fff 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -12,8 +12,10 @@ namespace Service::AM { class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { public: - explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) { + explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)), + msg_queue(std::move(msg_queue)) { // clang-format off static const FunctionInfo functions[] = { {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, @@ -70,7 +72,7 @@ private: void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(); + rb.PushIpcInterface<ICommonStateGetter>(msg_queue); LOG_DEBUG(Service_AM, "called"); } @@ -89,17 +91,20 @@ private: } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + std::shared_ptr<AppletMessageQueue> msg_queue; }; void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IApplicationProxy>(nvflinger); + rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue); LOG_DEBUG(Service_AM, "called"); } -AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) { +AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)), + msg_queue(std::move(msg_queue)) { static const FunctionInfo functions[] = { {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, }; @@ -108,4 +113,8 @@ AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) AppletOE::~AppletOE() = default; +const std::shared_ptr<AppletMessageQueue>& AppletOE::GetMessageQueue() const { + return msg_queue; +} + } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index 60cfdfd9d..bbd0108ef 100644 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h @@ -17,13 +17,17 @@ namespace AM { class AppletOE final : public ServiceFramework<AppletOE> { public: - explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); + explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, + std::shared_ptr<AppletMessageQueue> msg_queue); ~AppletOE() override; + const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const; + private: void OpenApplicationProxy(Kernel::HLERequestContext& ctx); std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + std::shared_ptr<AppletMessageQueue> msg_queue; }; } // namespace AM diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 428069df2..54305cf05 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -24,8 +24,8 @@ namespace Service::AOC { constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000; -static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { - return (aoc & DLC_BASE_TITLE_ID_MASK) == base; +static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) { + return (title_id & DLC_BASE_TITLE_ID_MASK) == base; } static std::vector<u64> AccumulateAOCTitleIDs() { @@ -74,7 +74,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); rb.Push<u32>(static_cast<u32>( std::count_if(add_on_content.begin(), add_on_content.end(), - [¤t](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; }))); + [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }))); } void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index fac6785a5..d3ea57ea7 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -28,13 +28,13 @@ public: {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"}, {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"}, {3, &IAudioRenderer::GetState, "GetState"}, - {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"}, + {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"}, {5, &IAudioRenderer::Start, "Start"}, {6, &IAudioRenderer::Stop, "Stop"}, {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, - {8, nullptr, "SetRenderingTimeLimit"}, - {9, nullptr, "GetRenderingTimeLimit"}, - {10, nullptr, "RequestUpdateAuto"}, + {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, + {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, + {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"}, {11, nullptr, "ExecuteAudioRendererRendering"}, }; // clang-format on @@ -79,7 +79,7 @@ private: LOG_DEBUG(Service_Audio, "called"); } - void RequestUpdate(Kernel::HLERequestContext& ctx) { + void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -110,8 +110,29 @@ private: LOG_WARNING(Service_Audio, "(STUBBED) called"); } + void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + rendering_time_limit_percent = rp.Pop<u32>(); + ASSERT(rendering_time_limit_percent >= 0 && rendering_time_limit_percent <= 100); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}", + rendering_time_limit_percent); + } + + void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(rendering_time_limit_percent); + } + Kernel::SharedPtr<Kernel::Event> system_event; std::unique_ptr<AudioCore::AudioRenderer> renderer; + u32 rendering_time_limit_percent = 100; }; class IAudioDevice final : public ServiceFramework<IAudioDevice> { diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 7168c6a10..763e619a4 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -77,8 +77,8 @@ private: IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(consumed); - rb.Push<u64>(performance); rb.Push<u32>(sample_count); + rb.Push<u64>(performance); ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); } @@ -161,7 +161,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); std::size_t worker_sz = WorkerBufferSize(channel_count); - ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large"); + ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ static_cast<OpusDecoder*>(operator new(worker_sz))}; if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) { diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index d0a15cc4c..f3bde6d0d 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -2,12 +2,49 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/btdrv/btdrv.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" namespace Service::BtDrv { +class Bt final : public ServiceFramework<Bt> { +public: + explicit Bt() : ServiceFramework{"bt"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown0"}, + {1, nullptr, "Unknown1"}, + {2, nullptr, "Unknown2"}, + {3, nullptr, "Unknown3"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "Unknown7"}, + {8, nullptr, "Unknown8"}, + {9, &Bt::RegisterEvent, "RegisterEvent"}, + }; + // clang-format on + RegisterHandlers(functions); + } + +private: + void RegisterEvent(Kernel::HLERequestContext& ctx) { + auto& kernel = Core::System::GetInstance().Kernel(); + register_event = + Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "BT:RegisterEvent"); + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(register_event); + LOG_WARNING(Service_BTM, "(STUBBED) called"); + } + Kernel::SharedPtr<Kernel::Event> register_event; +}; + class BtDrv final : public ServiceFramework<BtDrv> { public: explicit BtDrv() : ServiceFramework{"btdrv"} { @@ -67,6 +104,7 @@ public: void InstallInterfaces(SM::ServiceManager& sm) { std::make_shared<BtDrv>()->InstallAsService(sm); + std::make_shared<Bt>()->InstallAsService(sm); } } // namespace Service::BtDrv diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index b949bfabd..a02f6b53a 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -6,13 +6,118 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/btm/btm.h" #include "core/hle/service/service.h" -#include "core/hle/service/sm/sm.h" namespace Service::BTM { +class IBtmUserCore final : public ServiceFramework<IBtmUserCore> { +public: + explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IBtmUserCore::GetScanEvent, "GetScanEvent"}, + {1, nullptr, "Unknown1"}, + {2, nullptr, "Unknown2"}, + {3, nullptr, "Unknown3"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "Unknown7"}, + {8, nullptr, "Unknown8"}, + {9, nullptr, "Unknown9"}, + {10, nullptr, "Unknown10"}, + {17, &IBtmUserCore::GetConnectionEvent, "GetConnectionEvent"}, + {18, nullptr, "Unknown18"}, + {19, nullptr, "Unknown19"}, + {20, nullptr, "Unknown20"}, + {21, nullptr, "Unknown21"}, + {22, nullptr, "Unknown22"}, + {23, nullptr, "Unknown23"}, + {24, nullptr, "Unknown24"}, + {25, nullptr, "Unknown25"}, + {26, &IBtmUserCore::GetDiscoveryEvent, "AcquireBleServiceDiscoveryEventImpl"}, + {27, nullptr, "Unknown27"}, + {28, nullptr, "Unknown28"}, + {29, nullptr, "Unknown29"}, + {30, nullptr, "Unknown30"}, + {31, nullptr, "Unknown31"}, + {32, nullptr, "Unknown32"}, + {33, &IBtmUserCore::GetConfigEvent, "GetConfigEvent"}, + {34, nullptr, "Unknown34"}, + {35, nullptr, "Unknown35"}, + {36, nullptr, "Unknown36"}, + {37, nullptr, "Unknown37"}, + }; + // clang-format on + RegisterHandlers(functions); + } + +private: + void GetScanEvent(Kernel::HLERequestContext& ctx) { + auto& kernel = Core::System::GetInstance().Kernel(); + scan_event = + Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ScanEvent"); + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(scan_event); + LOG_WARNING(Service_BTM, "(STUBBED) called"); + } + void GetConnectionEvent(Kernel::HLERequestContext& ctx) { + auto& kernel = Core::System::GetInstance().Kernel(); + connection_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, + "IBtmUserCore:ConnectionEvent"); + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(connection_event); + LOG_WARNING(Service_BTM, "(STUBBED) called"); + } + void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) { + auto& kernel = Core::System::GetInstance().Kernel(); + service_discovery = + Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery"); + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(service_discovery); + LOG_WARNING(Service_BTM, "(STUBBED) called"); + } + void GetConfigEvent(Kernel::HLERequestContext& ctx) { + auto& kernel = Core::System::GetInstance().Kernel(); + config_event = + Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConfigEvent"); + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(config_event); + LOG_WARNING(Service_BTM, "(STUBBED) called"); + } + Kernel::SharedPtr<Kernel::Event> scan_event; + Kernel::SharedPtr<Kernel::Event> connection_event; + Kernel::SharedPtr<Kernel::Event> service_discovery; + Kernel::SharedPtr<Kernel::Event> config_event; +}; + +class BTM_USR final : public ServiceFramework<BTM_USR> { +public: + explicit BTM_USR() : ServiceFramework{"btm:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &BTM_USR::GetCoreImpl, "GetCoreImpl"}, + }; + // clang-format on + RegisterHandlers(functions); + } + +private: + void GetCoreImpl(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IBtmUserCore>(); + LOG_DEBUG(Service_BTM, "called"); + } +}; + class BTM final : public ServiceFramework<BTM> { public: explicit BTM() : ServiceFramework{"btm"} { @@ -116,6 +221,7 @@ void InstallInterfaces(SM::ServiceManager& sm) { std::make_shared<BTM>()->InstallAsService(sm); std::make_shared<BTM_DBG>()->InstallAsService(sm); std::make_shared<BTM_SYS>()->InstallAsService(sm); + std::make_shared<BTM_USR>()->InstallAsService(sm); } } // namespace Service::BTM diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index e32a7c48e..5d6294016 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -303,25 +303,42 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, static_cast<u8>(space), save_struct.DebugInfo()); if (save_data_factory == nullptr) { - return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound); + return FileSys::ERROR_ENTITY_NOT_FOUND; } return save_data_factory->Open(space, save_struct); } +ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { + LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space)); + + if (save_data_factory == nullptr) { + return FileSys::ERROR_ENTITY_NOT_FOUND; + } + + return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); +} + ResultVal<FileSys::VirtualDir> OpenSDMC() { LOG_TRACE(Service_FS, "Opening SDMC"); if (sdmc_factory == nullptr) { - return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound); + return FileSys::ERROR_SD_CARD_NOT_FOUND; } return sdmc_factory->Open(); } -std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { - return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{ - GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}); +std::shared_ptr<FileSys::RegisteredCacheUnion> registered_cache_union; + +std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { + if (registered_cache_union == nullptr) { + registered_cache_union = + std::make_shared<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{ + GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}); + } + + return registered_cache_union; } FileSys::RegisteredCache* GetSystemNANDContents() { @@ -360,6 +377,15 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) { return bis_factory->GetModificationLoadRoot(title_id); } +FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) { + LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id); + + if (bis_factory == nullptr) + return nullptr; + + return bis_factory->GetModificationDumpRoot(title_id); +} + void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { if (overwrite) { bis_factory = nullptr; @@ -373,13 +399,21 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { FileSys::Mode::ReadWrite); auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), FileSys::Mode::ReadWrite); + auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), + FileSys::Mode::ReadWrite); - if (bis_factory == nullptr) - bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory); - if (save_data_factory == nullptr) + if (bis_factory == nullptr) { + bis_factory = + std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); + } + + if (save_data_factory == nullptr) { save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); - if (sdmc_factory == nullptr) + } + + if (sdmc_factory == nullptr) { sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); + } } void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) { diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 6ca5c5636..ff9182e84 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -45,15 +45,17 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora FileSys::ContentRecordType type); ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, FileSys::SaveDataDescriptor save_struct); +ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); ResultVal<FileSys::VirtualDir> OpenSDMC(); -std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); +std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); FileSys::RegisteredCache* GetSystemNANDContents(); FileSys::RegisteredCache* GetUserNANDContents(); FileSys::RegisteredCache* GetSDMCContents(); FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); +FileSys::VirtualDir GetModificationDumpRoot(u64 title_id); // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function // above is called. diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index d5dced429..038dc80b1 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -11,12 +11,14 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/hex_util.h" #include "common/logging/log.h" #include "common/string_util.h" #include "core/file_sys/directory.h" #include "core/file_sys/errors.h" #include "core/file_sys/mode.h" #include "core/file_sys/nca_metadata.h" +#include "core/file_sys/patch_manager.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" @@ -61,12 +63,12 @@ private: // Error checking if (length < 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + rb.Push(FileSys::ERROR_INVALID_SIZE); return; } if (offset < 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + rb.Push(FileSys::ERROR_INVALID_OFFSET); return; } @@ -106,12 +108,12 @@ private: // Error checking if (length < 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + rb.Push(FileSys::ERROR_INVALID_SIZE); return; } if (offset < 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + rb.Push(FileSys::ERROR_INVALID_OFFSET); return; } @@ -137,12 +139,12 @@ private: // Error checking if (length < 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + rb.Push(FileSys::ERROR_INVALID_SIZE); return; } if (offset < 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + rb.Push(FileSys::ERROR_INVALID_OFFSET); return; } @@ -272,8 +274,8 @@ public: {0, &IFileSystem::CreateFile, "CreateFile"}, {1, &IFileSystem::DeleteFile, "DeleteFile"}, {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, - {3, nullptr, "DeleteDirectory"}, - {4, nullptr, "DeleteDirectoryRecursively"}, + {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"}, + {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"}, {5, &IFileSystem::RenameFile, "RenameFile"}, {6, nullptr, "RenameDirectory"}, {7, &IFileSystem::GetEntryType, "GetEntryType"}, @@ -328,6 +330,30 @@ public: rb.Push(backend.CreateDirectory(name)); } + void DeleteDirectory(Kernel::HLERequestContext& ctx) { + const IPC::RequestParser rp{ctx}; + + const auto file_buffer = ctx.ReadBuffer(); + std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called directory {}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.DeleteDirectory(name)); + } + + void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) { + const IPC::RequestParser rp{ctx}; + + const auto file_buffer = ctx.ReadBuffer(); + std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called directory {}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.DeleteDirectoryRecursively(name)); + } + void RenameFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -426,7 +452,147 @@ private: VfsDirectoryServiceWrapper backend; }; +class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { +public: + explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space) + : ServiceFramework("ISaveDataInfoReader") { + static const FunctionInfo functions[] = { + {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, + }; + RegisterHandlers(functions); + + FindAllSaves(space); + } + + void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) { + // Calculate how many entries we can fit in the output buffer + const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo); + + // Cap at total number of entries. + const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index); + + // Determine data start and end + const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index); + const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries); + const auto range_size = static_cast<std::size_t>(std::distance(begin, end)); + + next_entry_index += actual_entries; + + // Write the data to memory + ctx.WriteBuffer(begin, range_size); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(actual_entries)); + } + +private: + static u64 stoull_be(std::string_view str) { + if (str.size() != 16) + return 0; + + const auto bytes = Common::HexStringToArray<0x8>(str); + u64 out{}; + std::memcpy(&out, bytes.data(), sizeof(u64)); + + return Common::swap64(out); + } + + void FindAllSaves(FileSys::SaveDataSpaceId space) { + const auto save_root = OpenSaveDataSpace(space); + ASSERT(save_root.Succeeded()); + + for (const auto& type : (*save_root)->GetSubdirectories()) { + if (type->GetName() == "save") { + for (const auto& save_id : type->GetSubdirectories()) { + for (const auto& user_id : save_id->GetSubdirectories()) { + const auto save_id_numeric = stoull_be(save_id->GetName()); + auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName()); + std::reverse(user_id_numeric.begin(), user_id_numeric.end()); + + if (save_id_numeric != 0) { + // System Save Data + info.emplace_back(SaveDataInfo{ + 0, + space, + FileSys::SaveDataType::SystemSaveData, + {}, + user_id_numeric, + save_id_numeric, + 0, + user_id->GetSize(), + {}, + }); + + continue; + } + + for (const auto& title_id : user_id->GetSubdirectories()) { + const auto device = + std::all_of(user_id_numeric.begin(), user_id_numeric.end(), + [](u8 val) { return val == 0; }); + info.emplace_back(SaveDataInfo{ + 0, + space, + device ? FileSys::SaveDataType::DeviceSaveData + : FileSys::SaveDataType::SaveData, + {}, + user_id_numeric, + save_id_numeric, + stoull_be(title_id->GetName()), + title_id->GetSize(), + {}, + }); + } + } + } + } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) { + // Temporary Storage + for (const auto& user_id : type->GetSubdirectories()) { + for (const auto& title_id : user_id->GetSubdirectories()) { + if (!title_id->GetFiles().empty() || + !title_id->GetSubdirectories().empty()) { + auto user_id_numeric = + Common::HexStringToArray<0x10>(user_id->GetName()); + std::reverse(user_id_numeric.begin(), user_id_numeric.end()); + + info.emplace_back(SaveDataInfo{ + 0, + space, + FileSys::SaveDataType::TemporaryStorage, + {}, + user_id_numeric, + stoull_be(type->GetName()), + stoull_be(title_id->GetName()), + title_id->GetSize(), + {}, + }); + } + } + } + } + } + } + + struct SaveDataInfo { + u64_le save_id_unknown; + FileSys::SaveDataSpaceId space; + FileSys::SaveDataType type; + INSERT_PADDING_BYTES(0x6); + std::array<u8, 0x10> user_id; + u64_le save_id; + u64_le title_id; + u64_le save_image_size; + INSERT_PADDING_BYTES(0x28); + }; + static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); + + std::vector<SaveDataInfo> info; + u64 next_entry_index = 0; +}; + FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "MountContent"}, {1, &FSP_SRV::Initialize, "Initialize"}, @@ -460,7 +626,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {58, nullptr, "ReadSaveDataFileSystemExtraData"}, {59, nullptr, "WriteSaveDataFileSystemExtraData"}, {60, nullptr, "OpenSaveDataInfoReader"}, - {61, nullptr, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, + {61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, {62, nullptr, "OpenCacheStorageList"}, {64, nullptr, "OpenSaveDataInternalStorageFileSystem"}, {65, nullptr, "UpdateSaveDataMacForDebug"}, @@ -519,6 +685,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {1009, nullptr, "GetAndClearMemoryReportInfo"}, {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, }; + // clang-format on RegisterHandlers(functions); } @@ -577,7 +744,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { if (dir.Failed()) { IPC::ResponseBuilder rb{ctx, 2, 0, 0}; - rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); + rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); return; } @@ -593,6 +760,15 @@ void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) { MountSaveData(ctx); } +void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>(); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space)); +} + void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_FS, "(STUBBED) called"); @@ -630,6 +806,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { static_cast<u8>(storage_id), unknown, title_id); auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); + if (data.Failed()) { // TODO(DarkLordZach): Find the right error code to use here LOG_ERROR(Service_FS, @@ -640,7 +817,9 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { return; } - IStorage storage(std::move(data.Unwrap())); + FileSys::PatchManager pm{title_id}; + + IStorage storage(pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data)); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -657,7 +836,7 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { static_cast<u8>(storage_id), title_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); + rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); } } // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index 4aa0358cb..e7abec0a3 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -25,6 +25,7 @@ private: void CreateSaveData(Kernel::HLERequestContext& ctx); void MountSaveData(Kernel::HLERequestContext& ctx); void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); + void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx); void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index b06e65a77..205e4fd14 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -40,6 +40,29 @@ enum class JoystickId : std::size_t { Joystick_Right, }; +static std::size_t NPadIdToIndex(u32 npad_id) { + switch (npad_id) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + return npad_id; + case 8: + case NPAD_HANDHELD: + return 8; + case 9: + case NPAD_UNKNOWN: + return 9; + default: + UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id); + return 0; + } +} + Controller_NPad::Controller_NPad() = default; Controller_NPad::~Controller_NPad() = default; @@ -108,9 +131,10 @@ void Controller_NPad::OnInit() { styleset_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); - if (!IsControllerActivated()) + if (!IsControllerActivated()) { return; - std::size_t controller{}; + } + if (style.raw == 0) { // We want to support all controllers style.handheld.Assign(1); @@ -287,10 +311,11 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { switch (controller_type) { case NPadControllerType::Handheld: handheld_entry.connection_status.raw = 0; - handheld_entry.connection_status.IsConnected.Assign(1); - if (!Settings::values.use_docked_mode) { - handheld_entry.connection_status.IsWired.Assign(1); - } + handheld_entry.connection_status.IsWired.Assign(1); + handheld_entry.connection_status.IsLeftJoyConnected.Assign(1); + handheld_entry.connection_status.IsRightJoyConnected.Assign(1); + handheld_entry.connection_status.IsLeftJoyWired.Assign(1); + handheld_entry.connection_status.IsRightJoyWired.Assign(1); handheld_entry.pad_states.raw = pad_state.raw; handheld_entry.l_stick = lstick_entry; handheld_entry.r_stick = rstick_entry; @@ -309,6 +334,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { dual_entry.pad_states.raw = pad_state.raw; dual_entry.l_stick = lstick_entry; dual_entry.r_stick = rstick_entry; + break; case NPadControllerType::JoyLeft: left_entry.connection_status.raw = 0; @@ -369,16 +395,30 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { supported_npad_id_types.clear(); supported_npad_id_types.resize(length / sizeof(u32)); std::memcpy(supported_npad_id_types.data(), data, length); + bool had_controller_update = false; for (std::size_t i = 0; i < connected_controllers.size(); i++) { auto& controller = connected_controllers[i]; if (!controller.is_connected) { continue; } if (!IsControllerSupported(PREFERRED_CONTROLLER)) { - controller.type = DecideBestController(PREFERRED_CONTROLLER); - InitNewlyAddedControler(i); + const auto best_type = DecideBestController(PREFERRED_CONTROLLER); + const bool is_handheld = (best_type == NPadControllerType::Handheld || + PREFERRED_CONTROLLER == NPadControllerType::Handheld); + if (is_handheld) { + controller.type = NPadControllerType::None; + controller.is_connected = false; + AddNewController(best_type); + } else { + controller.type = best_type; + InitNewlyAddedControler(i); + } + had_controller_update = true; } } + if (had_controller_update) { + styleset_changed_event->Signal(); + } } void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { @@ -391,8 +431,10 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const { } void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { + styleset_changed_event->Signal(); hold_type = joy_hold_type; } + Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { return hold_type; } @@ -426,6 +468,9 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, } Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const { + // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should + // be signalled at least once, and signaled after a new controller is connected? + styleset_changed_event->Signal(); return styleset_changed_event; } @@ -451,15 +496,11 @@ void Controller_NPad::AddNewController(NPadControllerType controller) { } void Controller_NPad::ConnectNPad(u32 npad_id) { - if (npad_id >= connected_controllers.size()) - return; - connected_controllers[npad_id].is_connected = true; + connected_controllers[NPadIdToIndex(npad_id)].is_connected = true; } void Controller_NPad::DisconnectNPad(u32 npad_id) { - if (npad_id >= connected_controllers.size()) - return; - connected_controllers[npad_id].is_connected = false; + connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; } Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index a9aa9ec78..39631b14f 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -96,6 +96,8 @@ public: // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); + + ReloadInputDevices(); } void ActivateController(HidController controller) { @@ -284,10 +286,10 @@ public: {519, nullptr, "GetPalmaOperationResult"}, {520, nullptr, "ReadPalmaPlayLog"}, {521, nullptr, "ResetPalmaPlayLog"}, - {522, nullptr, "SetIsPalmaAllConnectable"}, + {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, {523, nullptr, "SetIsPalmaPairedConnectable"}, {524, nullptr, "PairPalma"}, - {525, nullptr, "SetPalmaBoostMode"}, + {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, {1000, nullptr, "SetNpadCommunicationMode"}, {1001, nullptr, "GetNpadCommunicationMode"}, }; @@ -594,6 +596,18 @@ private: rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_HID, "(STUBBED) called"); } + + void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } }; class HidDbg final : public ServiceFramework<HidDbg> { diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index ec32faf15..b43f1f054 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -3,12 +3,51 @@ // Refer to the license.txt file included. #include <memory> +#include <fmt/format.h> +#include <mbedtls/sha256.h> +#include "common/alignment.h" +#include "common/hex_util.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/ldr/ldr.h" #include "core/hle/service/service.h" +#include "core/loader/nro.h" namespace Service::LDR { +namespace ErrCodes { +enum { + InvalidMemoryState = 51, + InvalidNRO = 52, + InvalidNRR = 53, + MissingNRRHash = 54, + MaximumNRO = 55, + MaximumNRR = 56, + AlreadyLoaded = 57, + InvalidAlignment = 81, + InvalidSize = 82, + InvalidNROAddress = 84, + InvalidNRRAddress = 85, + NotInitialized = 87, +}; +} + +constexpr ResultCode ERROR_INVALID_MEMORY_STATE(ErrorModule::Loader, ErrCodes::InvalidMemoryState); +constexpr ResultCode ERROR_INVALID_NRO(ErrorModule::Loader, ErrCodes::InvalidNRO); +constexpr ResultCode ERROR_INVALID_NRR(ErrorModule::Loader, ErrCodes::InvalidNRR); +constexpr ResultCode ERROR_MISSING_NRR_HASH(ErrorModule::Loader, ErrCodes::MissingNRRHash); +constexpr ResultCode ERROR_MAXIMUM_NRO(ErrorModule::Loader, ErrCodes::MaximumNRO); +constexpr ResultCode ERROR_MAXIMUM_NRR(ErrorModule::Loader, ErrCodes::MaximumNRR); +constexpr ResultCode ERROR_ALREADY_LOADED(ErrorModule::Loader, ErrCodes::AlreadyLoaded); +constexpr ResultCode ERROR_INVALID_ALIGNMENT(ErrorModule::Loader, ErrCodes::InvalidAlignment); +constexpr ResultCode ERROR_INVALID_SIZE(ErrorModule::Loader, ErrCodes::InvalidSize); +constexpr ResultCode ERROR_INVALID_NRO_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNROAddress); +constexpr ResultCode ERROR_INVALID_NRR_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNRRAddress); +constexpr ResultCode ERROR_NOT_INITIALIZED(ErrorModule::Loader, ErrCodes::NotInitialized); + +constexpr u64 MAXIMUM_LOADED_RO = 0x40; + class DebugMonitor final : public ServiceFramework<DebugMonitor> { public: explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} { @@ -59,16 +98,392 @@ public: explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "LoadNro"}, - {1, nullptr, "UnloadNro"}, - {2, nullptr, "LoadNrr"}, - {3, nullptr, "UnloadNrr"}, - {4, nullptr, "Initialize"}, + {0, &RelocatableObject::LoadNro, "LoadNro"}, + {1, &RelocatableObject::UnloadNro, "UnloadNro"}, + {2, &RelocatableObject::LoadNrr, "LoadNrr"}, + {3, &RelocatableObject::UnloadNrr, "UnloadNrr"}, + {4, &RelocatableObject::Initialize, "Initialize"}, }; // clang-format on RegisterHandlers(functions); } + + void LoadNrr(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + rp.Skip(2, false); + const VAddr nrr_addr{rp.Pop<VAddr>()}; + const u64 nrr_size{rp.Pop<u64>()}; + + if (!initialized) { + LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NOT_INITIALIZED); + return; + } + + if (nrr.size() >= MAXIMUM_LOADED_RO) { + LOG_ERROR(Service_LDR, "Loading new NRR would exceed the maximum number of loaded NRRs " + "(0x40)! Failing..."); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_MAXIMUM_NRR); + return; + } + + // NRR Address does not fall on 0x1000 byte boundary + if (!Common::Is4KBAligned(nrr_addr)) { + LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ALIGNMENT); + return; + } + + // NRR Size is zero or causes overflow + if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) { + LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", + nrr_addr, nrr_size); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_SIZE); + return; + } + // Read NRR data from memory + std::vector<u8> nrr_data(nrr_size); + Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size); + NRRHeader header; + std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); + + if (header.magic != Common::MakeMagic('N', 'R', 'R', '0')) { + LOG_ERROR(Service_LDR, "NRR did not have magic 'NRR0' (actual {:08X})!", header.magic); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_NRR); + return; + } + + if (header.size != nrr_size) { + LOG_ERROR(Service_LDR, + "NRR header reported size did not match LoadNrr parameter size! " + "(header_size={:016X}, loadnrr_size={:016X})", + header.size, nrr_size); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_SIZE); + return; + } + + if (Core::CurrentProcess()->GetTitleID() != header.title_id) { + LOG_ERROR(Service_LDR, + "Attempting to load NRR with title ID other than current process. (actual " + "{:016X})!", + header.title_id); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_NRR); + return; + } + + std::vector<SHA256Hash> hashes; + + // Copy all hashes in the NRR (specified by hash count/hash offset) into vector. + for (std::size_t i = header.hash_offset; + i < (header.hash_offset + (header.hash_count * sizeof(SHA256Hash))); i += 8) { + SHA256Hash hash; + std::memcpy(hash.data(), nrr_data.data() + i, sizeof(SHA256Hash)); + hashes.emplace_back(hash); + } + + nrr.insert_or_assign(nrr_addr, std::move(hashes)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void UnloadNrr(Kernel::HLERequestContext& ctx) { + if (!initialized) { + LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NOT_INITIALIZED); + return; + } + + IPC::RequestParser rp{ctx}; + rp.Skip(2, false); + const auto nrr_addr{rp.Pop<VAddr>()}; + + if (!Common::Is4KBAligned(nrr_addr)) { + LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ALIGNMENT); + return; + } + + const auto iter = nrr.find(nrr_addr); + if (iter == nrr.end()) { + LOG_ERROR(Service_LDR, + "Attempting to unload NRR which has not been loaded! (addr={:016X})", + nrr_addr); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_NRR_ADDRESS); + return; + } + + nrr.erase(iter); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void LoadNro(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + rp.Skip(2, false); + const VAddr nro_addr{rp.Pop<VAddr>()}; + const u64 nro_size{rp.Pop<u64>()}; + const VAddr bss_addr{rp.Pop<VAddr>()}; + const u64 bss_size{rp.Pop<u64>()}; + + if (!initialized) { + LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NOT_INITIALIZED); + return; + } + + if (nro.size() >= MAXIMUM_LOADED_RO) { + LOG_ERROR(Service_LDR, "Loading new NRO would exceed the maximum number of loaded NROs " + "(0x40)! Failing..."); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_MAXIMUM_NRO); + return; + } + + // NRO Address does not fall on 0x1000 byte boundary + if (!Common::Is4KBAligned(nro_addr)) { + LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ALIGNMENT); + return; + } + + // NRO Size or BSS Size is zero or causes overflow + const auto nro_size_valid = + nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size); + const auto bss_size_valid = + nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr); + + if (!nro_size_valid || !bss_size_valid) { + LOG_ERROR(Service_LDR, + "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, " + "bss_address={:016X}, bss_size={:016X})", + nro_addr, nro_size, bss_addr, bss_size); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_SIZE); + return; + } + + // Read NRO data from memory + std::vector<u8> nro_data(nro_size); + Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); + + SHA256Hash hash{}; + mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0); + + // NRO Hash is already loaded + if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) { + return info.second.hash == hash; + })) { + LOG_ERROR(Service_LDR, "NRO is already loaded!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_ALREADY_LOADED); + return; + } + + // NRO Hash is not in any loaded NRR + if (!IsValidNROHash(hash)) { + LOG_ERROR(Service_LDR, + "NRO hash is not present in any currently loaded NRRs (hash={})!", + Common::HexArrayToString(hash)); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_MISSING_NRR_HASH); + return; + } + + NROHeader header; + std::memcpy(&header, nro_data.data(), sizeof(NROHeader)); + + if (!IsValidNRO(header, nro_size, bss_size)) { + LOG_ERROR(Service_LDR, "NRO was invalid!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_NRO); + return; + } + + // Load NRO as new executable module + auto* process = Core::CurrentProcess(); + auto& vm_manager = process->VMManager(); + auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size); + + if (!map_address.Succeeded() || + *map_address + nro_size + bss_size > vm_manager.GetAddressSpaceEndAddress()) { + + LOG_ERROR(Service_LDR, + "General error while allocation memory or no available memory to allocate!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_MEMORY_STATE); + return; + } + + ASSERT(process->MirrorMemory(*map_address, nro_addr, nro_size, + Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS); + ASSERT(process->UnmapMemory(nro_addr, 0, nro_size) == RESULT_SUCCESS); + + if (bss_size > 0) { + ASSERT(process->MirrorMemory(*map_address + nro_size, bss_addr, bss_size, + Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS); + ASSERT(process->UnmapMemory(bss_addr, 0, bss_size) == RESULT_SUCCESS); + } + + vm_manager.ReprotectRange(*map_address, header.text_size, + Kernel::VMAPermission::ReadExecute); + vm_manager.ReprotectRange(*map_address + header.ro_offset, header.ro_size, + Kernel::VMAPermission::Read); + vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size, + Kernel::VMAPermission::ReadWrite); + + Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); + + nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size}); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(*map_address); + } + + void UnloadNro(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + rp.Skip(2, false); + const VAddr mapped_addr{rp.PopRaw<VAddr>()}; + const VAddr heap_addr{rp.PopRaw<VAddr>()}; + + if (!initialized) { + LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NOT_INITIALIZED); + return; + } + + if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) { + LOG_ERROR(Service_LDR, + "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, " + "bss_addr={:016X})!", + mapped_addr, heap_addr); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ALIGNMENT); + return; + } + + const auto iter = nro.find(mapped_addr); + if (iter == nro.end()) { + LOG_ERROR(Service_LDR, + "The NRO attempting to unmap was not mapped or has an invalid address " + "(actual {:016X})!", + mapped_addr); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_NRO_ADDRESS); + return; + } + + auto* process = Core::CurrentProcess(); + auto& vm_manager = process->VMManager(); + const auto& nro_size = iter->second.size; + + ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size, + Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS); + ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS); + + Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); + + nro.erase(iter); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void Initialize(Kernel::HLERequestContext& ctx) { + initialized = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_LDR, "(STUBBED) called"); + } + +private: + using SHA256Hash = std::array<u8, 0x20>; + + struct NROHeader { + u32_le entrypoint_insn; + u32_le mod_offset; + INSERT_PADDING_WORDS(2); + u32_le magic; + INSERT_PADDING_WORDS(1); + u32_le nro_size; + INSERT_PADDING_WORDS(1); + u32_le text_offset; + u32_le text_size; + u32_le ro_offset; + u32_le ro_size; + u32_le rw_offset; + u32_le rw_size; + u32_le bss_size; + INSERT_PADDING_WORDS(1); + std::array<u8, 0x20> build_id; + INSERT_PADDING_BYTES(0x20); + }; + static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); + + struct NRRHeader { + u32_le magic; + INSERT_PADDING_BYTES(0x1C); + u64_le title_id_mask; + u64_le title_id_pattern; + std::array<u8, 0x100> modulus; + std::array<u8, 0x100> signature_1; + std::array<u8, 0x100> signature_2; + u64_le title_id; + u32_le size; + INSERT_PADDING_BYTES(4); + u32_le hash_offset; + u32_le hash_count; + INSERT_PADDING_BYTES(8); + }; + static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size."); + + struct NROInfo { + SHA256Hash hash; + u64 size; + }; + + bool initialized = false; + + std::map<VAddr, NROInfo> nro; + std::map<VAddr, std::vector<SHA256Hash>> nrr; + + bool IsValidNROHash(const SHA256Hash& hash) { + return std::any_of( + nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) { + return std::find(p.second.begin(), p.second.end(), hash) != p.second.end(); + }); + } + + static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { + return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && + header.nro_size == nro_size && header.bss_size == bss_size && + header.ro_offset == header.text_offset + header.text_size && + header.rw_offset == header.ro_offset + header.ro_size && + nro_size == header.rw_offset + header.rw_size && + Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && + Common::Is4KBAligned(header.rw_size); + } }; void InstallInterfaces(SM::ServiceManager& sm) { diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 8fec97db8..30e542542 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -10,12 +10,13 @@ #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" +#include "core/settings.h" namespace Service::NFC { class IAm final : public ServiceFramework<IAm> { public: - explicit IAm() : ServiceFramework{"IAm"} { + explicit IAm() : ServiceFramework{"NFC::IAm"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "Initialize"}, @@ -52,7 +53,7 @@ private: class MFIUser final : public ServiceFramework<MFIUser> { public: - explicit MFIUser() : ServiceFramework{"IUser"} { + explicit MFIUser() : ServiceFramework{"NFC::MFIUser"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "Initialize"}, @@ -100,13 +101,13 @@ private: class IUser final : public ServiceFramework<IUser> { public: - explicit IUser() : ServiceFramework{"IUser"} { + explicit IUser() : ServiceFramework{"NFC::IUser"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Initialize"}, - {1, nullptr, "Finalize"}, - {2, nullptr, "GetState"}, - {3, nullptr, "IsNfcEnabled"}, + {0, &IUser::InitializeOld, "InitializeOld"}, + {1, &IUser::FinalizeOld, "FinalizeOld"}, + {2, &IUser::GetStateOld, "GetStateOld"}, + {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"}, {400, nullptr, "Initialize"}, {401, nullptr, "Finalize"}, {402, nullptr, "GetState"}, @@ -130,11 +131,47 @@ public: RegisterHandlers(functions); } + +private: + enum class NfcStates : u32 { + Finalized = 6, + }; + + void InitializeOld(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0}; + rb.Push(RESULT_SUCCESS); + + // We don't deal with hardware initialization so we can just stub this. + LOG_DEBUG(Service_NFC, "called"); + } + + void IsNfcEnabledOld(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u8>(Settings::values.enable_nfc); + + LOG_DEBUG(Service_NFC, "IsNfcEnabledOld"); + } + + void GetStateOld(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NFC, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum(NfcStates::Finalized); // TODO(ogniK): Figure out if this matches nfp + } + + void FinalizeOld(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NFC, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } }; class NFC_U final : public ServiceFramework<NFC_U> { public: - explicit NFC_U() : ServiceFramework{"nfc:u"} { + explicit NFC_U() : ServiceFramework{"nfc:user"} { // clang-format off static const FunctionInfo functions[] = { {0, &NFC_U::CreateUserInterface, "CreateUserInterface"}, diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 39c0c1e63..1d6e7756f 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -2,56 +2,67 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <atomic> + #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" +#include "core/hle/lock.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nfp/nfp_user.h" namespace Service::NFP { +namespace ErrCodes { +constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP, + -1); // TODO(ogniK): Find the actual error code +} + Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) - : ServiceFramework(name), module(std::move(module)) {} + : ServiceFramework(name), module(std::move(module)) { + auto& kernel = Core::System::GetInstance().Kernel(); + nfc_tag_load = + Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:NFCTagDetected"); +} Module::Interface::~Interface() = default; class IUser final : public ServiceFramework<IUser> { public: - IUser() : ServiceFramework("IUser") { + IUser(Module::Interface& nfp_interface) + : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) { static const FunctionInfo functions[] = { {0, &IUser::Initialize, "Initialize"}, - {1, nullptr, "Finalize"}, + {1, &IUser::Finalize, "Finalize"}, {2, &IUser::ListDevices, "ListDevices"}, - {3, nullptr, "StartDetection"}, - {4, nullptr, "StopDetection"}, - {5, nullptr, "Mount"}, - {6, nullptr, "Unmount"}, - {7, nullptr, "OpenApplicationArea"}, - {8, nullptr, "GetApplicationArea"}, + {3, &IUser::StartDetection, "StartDetection"}, + {4, &IUser::StopDetection, "StopDetection"}, + {5, &IUser::Mount, "Mount"}, + {6, &IUser::Unmount, "Unmount"}, + {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, + {8, &IUser::GetApplicationArea, "GetApplicationArea"}, {9, nullptr, "SetApplicationArea"}, {10, nullptr, "Flush"}, {11, nullptr, "Restore"}, {12, nullptr, "CreateApplicationArea"}, - {13, nullptr, "GetTagInfo"}, - {14, nullptr, "GetRegisterInfo"}, - {15, nullptr, "GetCommonInfo"}, - {16, nullptr, "GetModelInfo"}, + {13, &IUser::GetTagInfo, "GetTagInfo"}, + {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, + {15, &IUser::GetCommonInfo, "GetCommonInfo"}, + {16, &IUser::GetModelInfo, "GetModelInfo"}, {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, {19, &IUser::GetState, "GetState"}, {20, &IUser::GetDeviceState, "GetDeviceState"}, {21, &IUser::GetNpadId, "GetNpadId"}, - {22, nullptr, "GetApplicationArea2"}, + {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, {24, nullptr, "RecreateApplicationArea"}, }; RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - activate_event = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:ActivateEvent"); deactivate_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, @@ -59,6 +70,17 @@ public: } private: + struct TagInfo { + std::array<u8, 10> uuid; + u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it + // mean something else + INSERT_PADDING_BYTES(0x15); + u32_le protocol; + u32_le tag_type; + INSERT_PADDING_BYTES(0x2c); + }; + static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); + enum class State : u32 { NonInitialized = 0, Initialized = 1, @@ -66,15 +88,40 @@ private: enum class DeviceState : u32 { Initialized = 0, + SearchingForTag = 1, + TagFound = 2, + TagRemoved = 3, + TagNearby = 4, + Unknown5 = 5, + Finalized = 6 + }; + + struct CommonInfo { + u16_be last_write_year; + u8 last_write_month; + u8 last_write_day; + u16_be write_counter; + u16_be version; + u32_be application_area_size; + INSERT_PADDING_BYTES(0x34); }; + static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); void Initialize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2, 0}; + rb.Push(RESULT_SUCCESS); state = State::Initialized; - IPC::ResponseBuilder rb{ctx, 2}; + LOG_DEBUG(Service_NFC, "called"); + } + + void GetState(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3, 0}; rb.Push(RESULT_SUCCESS); + rb.PushRaw<u32>(static_cast<u32>(state)); + + LOG_DEBUG(Service_NFC, "called"); } void ListDevices(Kernel::HLERequestContext& ctx) { @@ -83,80 +130,219 @@ private: ctx.WriteBuffer(&device_handle, sizeof(device_handle)); - LOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size); + LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0); + rb.Push<u32>(1); } - void AttachActivateEvent(Kernel::HLERequestContext& ctx) { + void GetNpadId(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 dev_handle = rp.Pop<u64>(); - LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(npad_id); + } + void AttachActivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 dev_handle = rp.Pop<u64>(); + LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(activate_event); + rb.PushCopyObjects(nfp_interface.GetNFCEvent()); + has_attached_handle = true; } void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 dev_handle = rp.Pop<u64>(); - LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(deactivate_event); } - void GetState(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 3}; + void StopDetection(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + switch (device_state) { + case DeviceState::TagFound: + case DeviceState::TagNearby: + deactivate_event->Signal(); + device_state = DeviceState::Initialized; + break; + case DeviceState::SearchingForTag: + case DeviceState::TagRemoved: + device_state = DeviceState::Initialized; + break; + } + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(static_cast<u32>(state)); } void GetDeviceState(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); + LOG_DEBUG(Service_NFP, "called"); + auto nfc_event = nfp_interface.GetNFCEvent(); + if (!nfc_event->ShouldWait(Kernel::GetCurrentThread()) && !has_attached_handle) { + device_state = DeviceState::TagFound; + nfc_event->Clear(); + } + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(static_cast<u32>(device_state)); } - void GetNpadId(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 dev_handle = rp.Pop<u64>(); - LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); - IPC::ResponseBuilder rb{ctx, 3}; + void StartDetection(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { + device_state = DeviceState::SearchingForTag; + } + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetTagInfo(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + auto amiibo = nfp_interface.GetAmiiboBuffer(); + TagInfo tag_info{}; + tag_info.uuid = amiibo.uuid; + tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size()); + + tag_info.protocol = 1; // TODO(ogniK): Figure out actual values + tag_info.tag_type = 2; + ctx.WriteBuffer(&tag_info, sizeof(TagInfo)); + rb.Push(RESULT_SUCCESS); + } + + void Mount(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + device_state = DeviceState::TagNearby; + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetModelInfo(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + auto amiibo = nfp_interface.GetAmiiboBuffer(); + ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info)); + rb.Push(RESULT_SUCCESS); + } + + void Unmount(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + device_state = DeviceState::TagFound; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void Finalize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + device_state = DeviceState::Finalized; + + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(npad_id); } void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 dev_handle = rp.Pop<u64>(); - LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + LOG_WARNING(Service_NFP, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(availability_change_event); } - const u64 device_handle{0xDEAD}; - const u32 npad_id{0}; // This is the first player controller id + void GetRegisterInfo(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NFP, "(STUBBED) called"); + + // TODO(ogniK): Pull Mii and owner data from amiibo + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetCommonInfo(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NFP, "(STUBBED) called"); + + // TODO(ogniK): Pull common information from amiibo + + CommonInfo common_info{}; + common_info.application_area_size = 0; + ctx.WriteBuffer(&common_info, sizeof(CommonInfo)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void OpenApplicationArea(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + // We don't need to worry about this since we can just open the file + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NFP, "(STUBBED) called"); + // We don't need to worry about this since we can just open the file + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub + } + + void GetApplicationArea(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NFP, "(STUBBED) called"); + + // TODO(ogniK): Pull application area from amiibo + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub + } + + bool has_attached_handle{}; + const u64 device_handle{Common::MakeMagic('Y', 'U', 'Z', 'U')}; + const u32 npad_id{0}; // Player 1 controller State state{State::NonInitialized}; DeviceState device_state{DeviceState::Initialized}; - Kernel::SharedPtr<Kernel::Event> activate_event; Kernel::SharedPtr<Kernel::Event> deactivate_event; Kernel::SharedPtr<Kernel::Event> availability_change_event; + const Module::Interface& nfp_interface; }; void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NFP, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IUser>(); + rb.PushIpcInterface<IUser>(*this); +} + +bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { + std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + if (buffer.size() < sizeof(AmiiboFile)) { + return false; + } + + std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); + nfc_tag_load->Signal(); + return true; +} +const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const { + return nfc_tag_load; +} +const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { + return amiibo; } void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 77df343c4..5c0ae8a54 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -4,6 +4,9 @@ #pragma once +#include <array> +#include <vector> +#include "core/hle/kernel/event.h" #include "core/hle/service/service.h" namespace Service::NFP { @@ -15,7 +18,27 @@ public: explicit Interface(std::shared_ptr<Module> module, const char* name); ~Interface() override; + struct ModelInfo { + std::array<u8, 0x8> amiibo_identification_block; + INSERT_PADDING_BYTES(0x38); + }; + static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); + + struct AmiiboFile { + std::array<u8, 10> uuid; + INSERT_PADDING_BYTES(0x4a); + ModelInfo model_info; + }; + static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); + void CreateUserInterface(Kernel::HLERequestContext& ctx); + bool LoadAmiibo(const std::vector<u8>& buffer); + const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const; + const AmiiboFile& GetAmiiboBuffer() const; + + private: + Kernel::SharedPtr<Kernel::Event> nfc_tag_load{}; + AmiiboFile amiibo{}; protected: std::shared_ptr<Module> module; diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 07c1381fe..1d2978f24 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -2,6 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/ns/ns.h" @@ -118,7 +121,7 @@ public: {305, nullptr, "TerminateSystemApplet"}, {306, nullptr, "LaunchOverlayApplet"}, {307, nullptr, "TerminateOverlayApplet"}, - {400, nullptr, "GetApplicationControlData"}, + {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"}, {401, nullptr, "InvalidateAllApplicationControlCache"}, {402, nullptr, "RequestDownloadApplicationControlData"}, {403, nullptr, "GetMaxApplicationControlCacheCount"}, @@ -243,6 +246,65 @@ public: RegisterHandlers(functions); } + + void GetApplicationControlData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto flag = rp.PopRaw<u64>(); + LOG_DEBUG(Service_NS, "called with flag={:016X}", flag); + + const auto title_id = rp.PopRaw<u64>(); + + const auto size = ctx.GetWriteBufferSize(); + + const FileSys::PatchManager pm{title_id}; + const auto control = pm.GetControlMetadata(); + + std::vector<u8> out; + + if (control.first != nullptr) { + if (size < 0x4000) { + LOG_ERROR(Service_NS, + "output buffer is too small! (actual={:016X}, expected_min=0x4000)", + size); + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(DarkLordZach): Find a better error code for this. + rb.Push(ResultCode(-1)); + return; + } + + out.resize(0x4000); + const auto bytes = control.first->GetRawBytes(); + std::memcpy(out.data(), bytes.data(), bytes.size()); + } else { + LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.", + title_id); + out.resize(std::min<u64>(0x4000, size)); + } + + if (control.second != nullptr) { + if (size < 0x4000 + control.second->GetSize()) { + LOG_ERROR(Service_NS, + "output buffer is too small! (actual={:016X}, expected_min={:016X})", + size, 0x4000 + control.second->GetSize()); + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(DarkLordZach): Find a better error code for this. + rb.Push(ResultCode(-1)); + return; + } + + out.resize(0x4000 + control.second->GetSize()); + control.second->Read(out.data() + 0x4000, control.second->GetSize()); + } else { + LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.", + title_id); + } + + ctx.WriteBuffer(out); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(out.size())); + } }; class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index 44accecb7..1066bf505 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp @@ -351,6 +351,14 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { font_sizes.push_back(region.size); } + // Resize buffers if game requests smaller size output. + font_codes.resize( + std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32))); + font_offsets.resize( + std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32))); + font_sizes.resize( + std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32))); + ctx.WriteBuffer(font_codes, 0); ctx.WriteBuffer(font_offsets, 1); ctx.WriteBuffer(font_sizes, 2); diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index fd98d541d..630ebbfc7 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -31,7 +31,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) buffer_wait_event->Signal(); } -boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { +std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { // Only consider free buffers. Buffers become free once again after they've been Acquired // and Released by the compositor, see the NVFlinger::Compose method. @@ -44,7 +44,7 @@ boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { }); if (itr == queue.end()) { - return boost::none; + return {}; } itr->status = Buffer::Status::Dequeued; @@ -70,12 +70,12 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, itr->crop_rect = crop_rect; } -boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() { +std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) { return buffer.status == Buffer::Status::Queued; }); if (itr == queue.end()) - return boost::none; + return {}; itr->status = Buffer::Status::Acquired; return *itr; } diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index 50b767732..8cff5eb71 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -4,8 +4,9 @@ #pragma once +#include <optional> #include <vector> -#include <boost/optional.hpp> + #include "common/common_funcs.h" #include "common/math_util.h" #include "common/swap.h" @@ -57,9 +58,9 @@ public: /// Rotate source image 90 degrees clockwise Rotate90 = 0x04, /// Rotate source image 180 degrees - Roate180 = 0x03, + Rotate180 = 0x03, /// Rotate source image 270 degrees clockwise - Roate270 = 0x07, + Rotate270 = 0x07, }; struct Buffer { @@ -73,11 +74,11 @@ public: }; void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); - boost::optional<u32> DequeueBuffer(u32 width, u32 height); + std::optional<u32> DequeueBuffer(u32 width, u32 height); const IGBPBuffer& RequestBuffer(u32 slot) const; void QueueBuffer(u32 slot, BufferTransformFlags transform, const MathUtil::Rectangle<int>& crop_rect); - boost::optional<const Buffer&> AcquireBuffer(); + std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); void ReleaseBuffer(u32 slot); u32 Query(QueryType type); diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index d47b6f659..214e6d1b3 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> -#include <boost/optional.hpp> +#include <optional> #include "common/alignment.h" #include "common/assert.h" @@ -134,7 +134,7 @@ void NVFlinger::Compose() { MicroProfileFlip(); - if (buffer == boost::none) { + if (!buffer) { auto& system_instance = Core::System::GetInstance(); // There was no queued buffer to draw, render previous frame @@ -143,7 +143,7 @@ void NVFlinger::Compose() { continue; } - auto& igbp_buffer = buffer->igbp_buffer; + auto& igbp_buffer = buffer->get().igbp_buffer; // Now send the buffer to the GPU for drawing. // TODO(Subv): Support more than just disp0. The display device selection is probably based @@ -152,10 +152,10 @@ void NVFlinger::Compose() { ASSERT(nvdisp); nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format, - igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform, - buffer->crop_rect); + igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, + buffer->get().transform, buffer->get().crop_rect); - buffer_queue->ReleaseBuffer(buffer->slot); + buffer_queue->ReleaseBuffer(buffer->get().slot); } } diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index a4cf45267..1ec340466 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -80,8 +80,8 @@ namespace Service { * Creates a function string for logging, complete with the name (or header code, depending * on what's passed in) the port name, and all the cmd_buff arguments. */ -static std::string MakeFunctionString(const char* name, const char* port_name, - const u32* cmd_buff) { +[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name, + const u32* cmd_buff) { // Number of params == bits 0-5 + bits 6-11 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp index 44a6717d0..b2de2a818 100644 --- a/src/core/hle/service/spl/module.cpp +++ b/src/core/hle/service/spl/module.cpp @@ -3,18 +3,23 @@ // Refer to the license.txt file included. #include <algorithm> +#include <chrono> #include <cstdlib> +#include <ctime> +#include <functional> #include <vector> #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/spl/csrng.h" #include "core/hle/service/spl/module.h" #include "core/hle/service/spl/spl.h" +#include "core/settings.h" namespace Service::SPL { Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) - : ServiceFramework(name), module(std::move(module)) {} + : ServiceFramework(name), module(std::move(module)), + rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {} Module::Interface::~Interface() = default; @@ -23,8 +28,9 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { std::size_t size = ctx.GetWriteBufferSize(); + std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max()); std::vector<u8> data(size); - std::generate(data.begin(), data.end(), std::rand); + std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); }); ctx.WriteBuffer(data); diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/module.h index 48fda6099..afa1f0295 100644 --- a/src/core/hle/service/spl/module.h +++ b/src/core/hle/service/spl/module.h @@ -4,6 +4,7 @@ #pragma once +#include <random> #include "core/hle/service/service.h" namespace Service::SPL { @@ -19,6 +20,9 @@ public: protected: std::shared_ptr<Module> module; + + private: + std::mt19937 rng; }; }; diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp index 18a5d71d5..b3a196f65 100644 --- a/src/core/hle/service/time/interface.cpp +++ b/src/core/hle/service/time/interface.cpp @@ -21,9 +21,10 @@ Time::Time(std::shared_ptr<Module> time, const char* name) {102, nullptr, "GetStandardUserSystemClockInitialYear"}, {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, - {400, nullptr, "GetClockSnapshot"}, + {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, - {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, + {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, + "CalculateStandardUserSystemClockDifferenceByUser"}, {501, nullptr, "CalculateSpanBetween"}, }; RegisterHandlers(functions); diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 28fd8debc..e561a0c52 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -15,6 +15,44 @@ namespace Service::Time { +static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time, + CalendarAdditionalInfo& additional_info, + [[maybe_unused]] const TimeZoneRule& /*rule*/) { + const std::time_t time(posix_time); + const std::tm* tm = std::localtime(&time); + if (tm == nullptr) { + calendar_time = {}; + additional_info = {}; + return; + } + calendar_time.year = tm->tm_year + 1900; + calendar_time.month = tm->tm_mon + 1; + calendar_time.day = tm->tm_mday; + calendar_time.hour = tm->tm_hour; + calendar_time.minute = tm->tm_min; + calendar_time.second = tm->tm_sec; + + additional_info.day_of_week = tm->tm_wday; + additional_info.day_of_year = tm->tm_yday; + std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC")); + additional_info.utc_offset = 0; +} + +static u64 CalendarToPosix(const CalendarTime& calendar_time, + [[maybe_unused]] const TimeZoneRule& /*rule*/) { + std::tm time{}; + time.tm_year = calendar_time.year - 1900; + time.tm_mon = calendar_time.month - 1; + time.tm_mday = calendar_time.day; + + time.tm_hour = calendar_time.hour; + time.tm_min = calendar_time.minute; + time.tm_sec = calendar_time.second; + + std::time_t epoch_time = std::mktime(&time); + return static_cast<u64>(epoch_time); +} + class ISystemClock final : public ServiceFramework<ISystemClock> { public: ISystemClock() : ServiceFramework("ISystemClock") { @@ -80,8 +118,8 @@ public: {5, nullptr, "GetTimeZoneRuleVersion"}, {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, - {201, nullptr, "ToPosixTime"}, - {202, nullptr, "ToPosixTimeWithMyRule"}, + {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"}, + {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"}, }; RegisterHandlers(functions); } @@ -151,24 +189,29 @@ private: rb.PushRaw(additional_info); } - void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time, - CalendarAdditionalInfo& additional_info, const TimeZoneRule& /*rule*/) { - std::time_t t(posix_time); - std::tm* tm = std::localtime(&t); - if (!tm) { - return; - } - calendar_time.year = tm->tm_year + 1900; - calendar_time.month = tm->tm_mon + 1; - calendar_time.day = tm->tm_mday; - calendar_time.hour = tm->tm_hour; - calendar_time.minute = tm->tm_min; - calendar_time.second = tm->tm_sec; - - additional_info.day_of_week = tm->tm_wday; - additional_info.day_of_year = tm->tm_yday; - std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC")); - additional_info.utc_offset = 0; + void ToPosixTime(Kernel::HLERequestContext& ctx) { + // TODO(ogniK): Figure out how to handle multiple times + LOG_WARNING(Service_Time, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + auto calendar_time = rp.PopRaw<CalendarTime>(); + auto posix_time = CalendarToPosix(calendar_time, {}); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u32>(1); // Amount of times we're returning + ctx.WriteBuffer(&posix_time, sizeof(u64)); + } + + void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Time, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + auto calendar_time = rp.PopRaw<CalendarTime>(); + auto posix_time = CalendarToPosix(calendar_time, {}); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u32>(1); // Amount of times we're returning + ctx.WriteBuffer(&posix_time, sizeof(u64)); } }; @@ -207,6 +250,70 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c LOG_DEBUG(Service_Time, "called"); } +void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + + IPC::RequestParser rp{ctx}; + auto unknown_u8 = rp.PopRaw<u8>(); + + ClockSnapshot clock_snapshot{}; + + const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count()}; + CalendarTime calendar_time{}; + const std::time_t time(time_since_epoch); + const std::tm* tm = std::localtime(&time); + if (tm == nullptr) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code + return; + } + SteadyClockTimePoint steady_clock_time_point{CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / + 1000}; + + LocationName location_name{"UTC"}; + calendar_time.year = tm->tm_year + 1900; + calendar_time.month = tm->tm_mon + 1; + calendar_time.day = tm->tm_mday; + calendar_time.hour = tm->tm_hour; + calendar_time.minute = tm->tm_min; + calendar_time.second = tm->tm_sec; + clock_snapshot.system_posix_time = time_since_epoch; + clock_snapshot.network_posix_time = time_since_epoch; + clock_snapshot.system_calendar_time = calendar_time; + clock_snapshot.network_calendar_time = calendar_time; + + CalendarAdditionalInfo additional_info{}; + PosixToCalendar(time_since_epoch, calendar_time, additional_info, {}); + + clock_snapshot.system_calendar_info = additional_info; + clock_snapshot.network_calendar_info = additional_info; + + clock_snapshot.steady_clock_timepoint = steady_clock_time_point; + clock_snapshot.location_name = location_name; + clock_snapshot.clock_auto_adjustment_enabled = 1; + clock_snapshot.ipc_u8 = unknown_u8; + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot)); +} + +void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser( + Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + + IPC::RequestParser rp{ctx}; + const auto snapshot_a = rp.PopRaw<ClockSnapshot>(); + const auto snapshot_b = rp.PopRaw<ClockSnapshot>(); + const u64 difference = + snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset; + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u64>(difference); +} + Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) : ServiceFramework(name), time(std::move(time)) {} diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index 5659ecad3..ea43fbea7 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -5,6 +5,7 @@ #pragma once #include <array> +#include "common/common_funcs.h" #include "core/hle/service/service.h" namespace Service::Time { @@ -53,6 +54,23 @@ struct SystemClockContext { static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext structure has incorrect size"); +struct ClockSnapshot { + SystemClockContext user_clock_context; + SystemClockContext network_clock_context; + s64_le system_posix_time; + s64_le network_posix_time; + CalendarTime system_calendar_time; + CalendarTime network_calendar_time; + CalendarAdditionalInfo system_calendar_info; + CalendarAdditionalInfo network_calendar_info; + SteadyClockTimePoint steady_clock_timepoint; + LocationName location_name; + u8 clock_auto_adjustment_enabled; + u8 ipc_u8; + INSERT_PADDING_BYTES(2); +}; +static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size"); + class Module final { public: class Interface : public ServiceFramework<Interface> { @@ -65,6 +83,8 @@ public: void GetStandardSteadyClock(Kernel::HLERequestContext& ctx); void GetTimeZoneService(Kernel::HLERequestContext& ctx); void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); + void GetClockSnapshot(Kernel::HLERequestContext& ctx); + void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx); protected: std::shared_ptr<Module> time; diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp index e7fb5a419..f0a831d45 100644 --- a/src/core/hle/service/usb/usb.cpp +++ b/src/core/hle/service/usb/usb.cpp @@ -67,15 +67,15 @@ public: explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Unknown1"}, - {1, nullptr, "Unknown2"}, - {2, nullptr, "Unknown3"}, - {3, nullptr, "Unknown4"}, + {0, nullptr, "Open"}, + {1, nullptr, "Close"}, + {2, nullptr, "Unknown1"}, + {3, nullptr, "Populate"}, {4, nullptr, "PostBufferAsync"}, - {5, nullptr, "Unknown5"}, - {6, nullptr, "Unknown6"}, - {7, nullptr, "Unknown7"}, - {8, nullptr, "Unknown8"}, + {5, nullptr, "GetXferReport"}, + {6, nullptr, "Unknown2"}, + {7, nullptr, "Unknown3"}, + {8, nullptr, "Unknown4"}, }; // clang-format on @@ -89,15 +89,15 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "Unknown1"}, - {1, nullptr, "Unknown2"}, - {2, nullptr, "Unknown3"}, - {3, nullptr, "Unknown4"}, - {4, nullptr, "Unknown5"}, + {1, nullptr, "SetInterface"}, + {2, nullptr, "GetInterface"}, + {3, nullptr, "GetAlternateInterface"}, + {4, nullptr, "GetCurrentFrame"}, {5, nullptr, "CtrlXferAsync"}, - {6, nullptr, "Unknown6"}, + {6, nullptr, "Unknown2"}, {7, nullptr, "GetCtrlXferReport"}, - {8, nullptr, "Unknown7"}, - {9, nullptr, "GetClientEpSession"}, + {8, nullptr, "ResetDevice"}, + {9, nullptr, "OpenUsbEp"}, }; // clang-format on @@ -111,13 +111,14 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "BindClientProcess"}, - {1, nullptr, "Unknown1"}, - {2, nullptr, "Unknown2"}, - {3, nullptr, "Unknown3"}, - {4, nullptr, "Unknown4"}, - {5, nullptr, "Unknown5"}, + {1, nullptr, "QueryAllInterfaces"}, + {2, nullptr, "QueryAvailableInterfaces"}, + {3, nullptr, "QueryAcquiredInterfaces"}, + {4, nullptr, "CreateInterfaceAvailableEvent"}, + {5, nullptr, "DestroyInterfaceAvailableEvent"}, {6, nullptr, "GetInterfaceStateChangeEvent"}, - {7, nullptr, "GetClientIfSession"}, + {7, nullptr, "AcquireUsbIf"}, + {8, nullptr, "Unknown1"}, }; // clang-format on @@ -131,11 +132,11 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "BindNoticeEvent"}, - {1, nullptr, "Unknown1"}, + {1, nullptr, "UnbindNoticeEvent"}, {2, nullptr, "GetStatus"}, {3, nullptr, "GetNotice"}, - {4, nullptr, "Unknown2"}, - {5, nullptr, "Unknown3"}, + {4, nullptr, "EnablePowerRequestNotice"}, + {5, nullptr, "DisablePowerRequestNotice"}, {6, nullptr, "ReplyPowerRequest"}, }; // clang-format on diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 184537daa..d25fdb1fe 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -6,9 +6,10 @@ #include <array> #include <cstring> #include <memory> +#include <optional> #include <type_traits> #include <utility> -#include <boost/optional.hpp> + #include "common/alignment.h" #include "common/assert.h" #include "common/common_funcs.h" @@ -236,6 +237,22 @@ private: Data data{}; }; +/// Represents a parcel containing one int '0' as its data +/// Used by DetachBuffer and Disconnect +class IGBPEmptyResponseParcel : public Parcel { +protected: + void SerializeData() override { + Write(data); + } + +private: + struct Data { + u32_le unk_0; + }; + + Data data{}; +}; + class IGBPSetPreallocatedBufferRequestParcel : public Parcel { public: explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer) @@ -506,9 +523,9 @@ private: IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; const u32 width{request.data.width}; const u32 height{request.data.height}; - boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); - if (slot != boost::none) { + if (slot) { // Buffer is available IGBPDequeueBufferResponseParcel response{*slot}; ctx.WriteBuffer(response.Serialize()); @@ -520,7 +537,7 @@ private: Kernel::ThreadWakeupReason reason) { // Repeat TransactParcel DequeueBuffer when a buffer is available auto buffer_queue = nv_flinger->GetBufferQueue(id); - boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); IGBPDequeueBufferResponseParcel response{*slot}; ctx.WriteBuffer(response.Serialize()); IPC::ResponseBuilder rb{ctx, 2}; @@ -553,6 +570,12 @@ private: ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::CancelBuffer) { LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); + } else if (transaction == TransactionId::Disconnect || + transaction == TransactionId::DetachBuffer) { + const auto buffer = ctx.ReadBuffer(); + + IGBPEmptyResponseParcel response{}; + ctx.WriteBuffer(response.Serialize()); } else { ASSERT_MSG(false, "Unimplemented"); } |