diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 99602699a..49af93604 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -459,8 +459,12 @@ add_library(core STATIC hle/service/fgm/fgm.h hle/service/friend/friend.cpp hle/service/friend/friend.h - hle/service/friend/friend_interface.cpp - hle/service/friend/friend_interface.h + hle/service/friend/friend_results.h + hle/service/friend/friend_service.cpp + hle/service/friend/friend_service.h + hle/service/friend/friend_types.h + hle/service/friend/notification_service.cpp + hle/service/friend/notification_service.h hle/service/glue/arp.cpp hle/service/glue/arp.h hle/service/glue/bgtc.cpp diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 9d05f9801..6a54eb81b 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include "common/logging/log.h" #include "common/uuid.h" #include "core/core.h" @@ -14,6 +13,9 @@ #include "core/hle/service/server_manager.h" namespace Service::Friend { +Module::Interface::Interface(std::shared_ptr module_, Core::System& system_, + const char* name) + : ServiceFramework{system_, name}, module{std::move(module_)} {} class IFriendService final : public ServiceFramework { public: @@ -330,11 +332,17 @@ void Module::Interface::CreateNotificationService(HLERequestContext& ctx) { rb.PushIpcInterface(system, uuid); } -Module::Interface::Interface(std::shared_ptr module_, Core::System& system_, - const char* name) - : ServiceFramework{system_, name}, module{std::move(module_)} {} +Friend::Friend(std::shared_ptr module_, Core::System& system_, const char* name) + : Interface(std::move(module_), system_, name) { + static const FunctionInfo functions[] = { + {0, &Friend::CreateFriendService, "CreateFriendService"}, + {1, &Friend::CreateNotificationService, "CreateNotificationService"}, + {2, nullptr, "CreateDaemonSuspendSessionService"}, + }; + RegisterHandlers(functions); +} -Module::Interface::~Interface() = default; +Friend::~Friend() = default; void LoopProcess(Core::System& system) { auto server_manager = std::make_unique(system); diff --git a/src/core/hle/service/friend/friend_interface.cpp b/src/core/hle/service/friend/friend_interface.cpp deleted file mode 100644 index c8b98b1f0..000000000 --- a/src/core/hle/service/friend/friend_interface.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/friend/friend_interface.h" - -namespace Service::Friend { - -Friend::Friend(std::shared_ptr module_, Core::System& system_, const char* name) - : Interface(std::move(module_), system_, name) { - static const FunctionInfo functions[] = { - {0, &Friend::CreateFriendService, "CreateFriendService"}, - {1, &Friend::CreateNotificationService, "CreateNotificationService"}, - {2, nullptr, "CreateDaemonSuspendSessionService"}, - }; - RegisterHandlers(functions); -} - -Friend::~Friend() = default; - -} // namespace Service::Friend diff --git a/src/core/hle/service/friend/friend_interface.h b/src/core/hle/service/friend/friend_interface.h deleted file mode 100644 index 3a2184c34..000000000 --- a/src/core/hle/service/friend/friend_interface.h +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/friend/friend.h" - -namespace Service::Friend { - -class Friend final : public Module::Interface { -public: - explicit Friend(std::shared_ptr module_, Core::System& system_, const char* name); - ~Friend() override; -}; - -} // namespace Service::Friend diff --git a/src/core/hle/service/friend/friend_results.h b/src/core/hle/service/friend/friend_results.h new file mode 100644 index 000000000..1950b0e78 --- /dev/null +++ b/src/core/hle/service/friend/friend_results.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::Friend { + +constexpr Result NotInitialized{ErrorModule::Account, 1}; +constexpr Result InvalidArgument{ErrorModule::Account, 2}; +constexpr Result NoNotifications{ErrorModule::Account, 15}; + +} // namespace Service::Friend diff --git a/src/core/hle/service/friend/friend_service.cpp b/src/core/hle/service/friend/friend_service.cpp new file mode 100644 index 000000000..4e59295a4 --- /dev/null +++ b/src/core/hle/service/friend/friend_service.cpp @@ -0,0 +1,184 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "common/uuid.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/friend/friend_results.h" +#include "core/hle/service/friend/friend_service.h" +#include "core/hle/service/friend/friend_types.h" + +namespace Service::Friend { + +IFriendService::IFriendService(Core::System& system_) + : ServiceFramework{system_, "IFriendService"}, service_context{system, "IFriendService"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IFriendService::GetCompletionEvent, "GetCompletionEvent"}, + {1, nullptr, "Cancel"}, + {10100, nullptr, "GetFriendListIds"}, + {10101, &IFriendService::GetFriendList, "GetFriendList"}, + {10102, nullptr, "UpdateFriendInfo"}, + {10110, nullptr, "GetFriendProfileImage"}, + {10120, &IFriendService::CheckFriendListAvailability, "CheckFriendListAvailability"}, + {10121, nullptr, "EnsureFriendListAvailable"}, + {10200, nullptr, "SendFriendRequestForApplication"}, + {10211, nullptr, "AddFacedFriendRequestForApplication"}, + {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"}, + {10420, nullptr, "IsBlockedUserListCacheAvailable"}, + {10421, nullptr, "EnsureBlockedUserListAvailable"}, + {10500, nullptr, "GetProfileList"}, + {10600, nullptr, "DeclareOpenOnlinePlaySession"}, + {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"}, + {10610, &IFriendService::UpdateUserPresence, "UpdateUserPresence"}, + {10700, &IFriendService::GetPlayHistoryRegistrationKey, "GetPlayHistoryRegistrationKey"}, + {10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"}, + {10702, nullptr, "AddPlayHistory"}, + {11000, nullptr, "GetProfileImageUrl"}, + {20100, nullptr, "GetFriendCount"}, + {20101, nullptr, "GetNewlyFriendCount"}, + {20102, nullptr, "GetFriendDetailedInfo"}, + {20103, nullptr, "SyncFriendList"}, + {20104, nullptr, "RequestSyncFriendList"}, + {20110, nullptr, "LoadFriendSetting"}, + {20200, nullptr, "GetReceivedFriendRequestCount"}, + {20201, nullptr, "GetFriendRequestList"}, + {20300, nullptr, "GetFriendCandidateList"}, + {20301, nullptr, "GetNintendoNetworkIdInfo"}, + {20302, nullptr, "GetSnsAccountLinkage"}, + {20303, nullptr, "GetSnsAccountProfile"}, + {20304, nullptr, "GetSnsAccountFriendList"}, + {20400, nullptr, "GetBlockedUserList"}, + {20401, nullptr, "SyncBlockedUserList"}, + {20500, nullptr, "GetProfileExtraList"}, + {20501, nullptr, "GetRelationship"}, + {20600, nullptr, "GetUserPresenceView"}, + {20700, nullptr, "GetPlayHistoryList"}, + {20701, nullptr, "GetPlayHistoryStatistics"}, + {20800, nullptr, "LoadUserSetting"}, + {20801, nullptr, "SyncUserSetting"}, + {20900, nullptr, "RequestListSummaryOverlayNotification"}, + {21000, nullptr, "GetExternalApplicationCatalog"}, + {22000, nullptr, "GetReceivedFriendInvitationList"}, + {22001, nullptr, "GetReceivedFriendInvitationDetailedInfo"}, + {22010, nullptr, "GetReceivedFriendInvitationCountCache"}, + {30100, nullptr, "DropFriendNewlyFlags"}, + {30101, nullptr, "DeleteFriend"}, + {30110, nullptr, "DropFriendNewlyFlag"}, + {30120, nullptr, "ChangeFriendFavoriteFlag"}, + {30121, nullptr, "ChangeFriendOnlineNotificationFlag"}, + {30200, nullptr, "SendFriendRequest"}, + {30201, nullptr, "SendFriendRequestWithApplicationInfo"}, + {30202, nullptr, "CancelFriendRequest"}, + {30203, nullptr, "AcceptFriendRequest"}, + {30204, nullptr, "RejectFriendRequest"}, + {30205, nullptr, "ReadFriendRequest"}, + {30210, nullptr, "GetFacedFriendRequestRegistrationKey"}, + {30211, nullptr, "AddFacedFriendRequest"}, + {30212, nullptr, "CancelFacedFriendRequest"}, + {30213, nullptr, "GetFacedFriendRequestProfileImage"}, + {30214, nullptr, "GetFacedFriendRequestProfileImageFromPath"}, + {30215, nullptr, "SendFriendRequestWithExternalApplicationCatalogId"}, + {30216, nullptr, "ResendFacedFriendRequest"}, + {30217, nullptr, "SendFriendRequestWithNintendoNetworkIdInfo"}, + {30300, nullptr, "GetSnsAccountLinkPageUrl"}, + {30301, nullptr, "UnlinkSnsAccount"}, + {30400, nullptr, "BlockUser"}, + {30401, nullptr, "BlockUserWithApplicationInfo"}, + {30402, nullptr, "UnblockUser"}, + {30500, nullptr, "GetProfileExtraFromFriendCode"}, + {30700, nullptr, "DeletePlayHistory"}, + {30810, nullptr, "ChangePresencePermission"}, + {30811, nullptr, "ChangeFriendRequestReception"}, + {30812, nullptr, "ChangePlayLogPermission"}, + {30820, nullptr, "IssueFriendCode"}, + {30830, nullptr, "ClearPlayLog"}, + {30900, nullptr, "SendFriendInvitation"}, + {30910, nullptr, "ReadFriendInvitation"}, + {30911, nullptr, "ReadAllFriendInvitations"}, + {40100, nullptr, "DeleteFriendListCache"}, + {40400, nullptr, "DeleteBlockedUserListCache"}, + {49900, nullptr, "DeleteNetworkServiceAccountCache"}, + }; + // clang-format on + + RegisterHandlers(functions); + + completion_event = service_context.CreateEvent("IFriendService:CompletionEvent"); +} + +IFriendService::~IFriendService() { + service_context.CloseEvent(completion_event); +} + +void IFriendService::GetCompletionEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Friend, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(completion_event->GetReadableEvent()); +} + +void IFriendService::GetBlockedUserListIds(Kernel::HLERequestContext& ctx) { + // This is safe to stub, as there should be no adverse consequences from reporting no + // blocked users. + LOG_WARNING(Service_Friend, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); // Indicates there are no blocked users +} + +void IFriendService::DeclareCloseOnlinePlaySession(Kernel::HLERequestContext& ctx) { + // Stub used by Splatoon 2 + LOG_WARNING(Service_Friend, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IFriendService::UpdateUserPresence(Kernel::HLERequestContext& ctx) { + // Stub used by Retro City Rampage + LOG_WARNING(Service_Friend, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IFriendService::GetPlayHistoryRegistrationKey(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto local_play = rp.Pop(); + const auto uuid = rp.PopRaw(); + + LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play, + uuid.RawString()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IFriendService::GetFriendList(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto friend_offset = rp.Pop(); + const auto uuid = rp.PopRaw(); + [[maybe_unused]] const auto filter = rp.PopRaw(); + const auto pid = rp.Pop(); + LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset, + uuid.RawString(), pid); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + + rb.Push(0); // Friend count + // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId" +} + +void IFriendService::CheckFriendListAvailability(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto uuid{rp.PopRaw()}; + + LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString()); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(true); +} + +} // namespace Service::Friend diff --git a/src/core/hle/service/friend/friend_service.h b/src/core/hle/service/friend/friend_service.h new file mode 100644 index 000000000..a39f04fba --- /dev/null +++ b/src/core/hle/service/friend/friend_service.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/friend/friend.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Service::Friend { + +class IFriendService final : public ServiceFramework { +public: + explicit IFriendService(Core::System& system_); + ~IFriendService() override; + +private: + void GetCompletionEvent(Kernel::HLERequestContext& ctx); + void GetFriendList(Kernel::HLERequestContext& ctx); + void CheckFriendListAvailability(Kernel::HLERequestContext& ctx); + void GetBlockedUserListIds(Kernel::HLERequestContext& ctx); + void DeclareCloseOnlinePlaySession(Kernel::HLERequestContext& ctx); + void UpdateUserPresence(Kernel::HLERequestContext& ctx); + void GetPlayHistoryRegistrationKey(Kernel::HLERequestContext& ctx); + + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* completion_event; +}; + +} // namespace Service::Friend diff --git a/src/core/hle/service/friend/friend_types.h b/src/core/hle/service/friend/friend_types.h new file mode 100644 index 000000000..9f2978986 --- /dev/null +++ b/src/core/hle/service/friend/friend_types.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/common_types.h" + +namespace Service::Friend { + +enum class PresenceFilter : u32 { + None = 0, + Online = 1, + OnlinePlay = 2, + OnlineOrOnlinePlay = 3, +}; + +struct SizedFriendFilter { + PresenceFilter presence; + u8 is_favorite; + u8 same_app; + u8 same_app_played; + u8 arbitary_app_played; + u64 group_id; +}; +static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size"); + +enum class NotificationTypes : u32 { + HasUpdatedFriendsList = 0x65, + HasReceivedFriendRequest = 0x1, +}; + +struct SizedNotificationInfo { + NotificationTypes notification_type; + INSERT_PADDING_WORDS( + 1); // TODO(ogniK): This doesn't seem to be used within any IPC returns as of now + u64_le account_id; +}; +static_assert(sizeof(SizedNotificationInfo) == 0x10, "SizedNotificationInfo is an incorrect size"); + +struct States { + bool has_updated_friends; + bool has_received_friend_request; +}; + +} // namespace Service::Friend diff --git a/src/core/hle/service/friend/notification_service.cpp b/src/core/hle/service/friend/notification_service.cpp new file mode 100644 index 000000000..9c34259e3 --- /dev/null +++ b/src/core/hle/service/friend/notification_service.cpp @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/friend/friend_results.h" +#include "core/hle/service/friend/friend_types.h" +#include "core/hle/service/friend/notification_service.h" + +namespace Service::Friend { + +INotificationService::INotificationService(Core::System& system_, Common::UUID uuid_) + : ServiceFramework{system_, "INotificationService"}, uuid{uuid_}, service_context{ + system_, + "INotificationService"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &INotificationService::GetEvent, "GetEvent"}, + {1, &INotificationService::Clear, "Clear"}, + {2, &INotificationService::Pop, "Pop"} + }; + // clang-format on + + RegisterHandlers(functions); + + notification_event = service_context.CreateEvent("INotificationService:NotifyEvent"); +} + +INotificationService::~INotificationService() { + service_context.CloseEvent(notification_event); +} + +void INotificationService::GetEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Friend, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(notification_event->GetReadableEvent()); +} + +void INotificationService::Clear(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Friend, "called"); + while (!notifications.empty()) { + notifications.pop(); + } + std::memset(&states, 0, sizeof(States)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void INotificationService::Pop(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Friend, "called"); + + if (notifications.empty()) { + LOG_ERROR(Service_Friend, "No notifications in queue!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NoNotifications); + return; + } + + const auto notification = notifications.front(); + notifications.pop(); + + switch (notification.notification_type) { + case NotificationTypes::HasUpdatedFriendsList: + states.has_updated_friends = false; + break; + case NotificationTypes::HasReceivedFriendRequest: + states.has_received_friend_request = false; + break; + default: + // HOS seems not have an error case for an unknown notification + LOG_WARNING(Service_Friend, "Unknown notification {:08X}", notification.notification_type); + break; + } + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(notification); +} + +} // namespace Service::Friend diff --git a/src/core/hle/service/friend/notification_service.h b/src/core/hle/service/friend/notification_service.h new file mode 100644 index 000000000..86c5a0319 --- /dev/null +++ b/src/core/hle/service/friend/notification_service.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once +#include + +#include "common/uuid.h" +#include "core/hle/service/friend/friend_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Service::Friend { + +class INotificationService final : public ServiceFramework { +public: + explicit INotificationService(Core::System& system_, Common::UUID uuid_); + ~INotificationService() override; + +private: + void GetEvent(Kernel::HLERequestContext& ctx); + void Clear(Kernel::HLERequestContext& ctx); + void Pop(Kernel::HLERequestContext& ctx); + + Common::UUID uuid; + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* notification_event; + std::queue notifications; + States states{}; +}; + +} // namespace Service::Friend