1 // Copyright 2018 yuzu emulator team
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4
5 #include <queue>
6 #include "common/logging/log.h"
7 #include "common/uuid.h"
8 #include "core/core.h"
9 #include "core/hle/ipc_helpers.h"
10 #include "core/hle/kernel/readable_event.h"
11 #include "core/hle/kernel/writable_event.h"
12 #include "core/hle/service/friend/errors.h"
13 #include "core/hle/service/friend/friend.h"
14 #include "core/hle/service/friend/interface.h"
15
16 namespace Service::Friend {
17
18 class IFriendService final : public ServiceFramework<IFriendService> {
19 public:
IFriendService(Core::System & system_)20 explicit IFriendService(Core::System& system_) : ServiceFramework{system_, "IFriendService"} {
21 // clang-format off
22 static const FunctionInfo functions[] = {
23 {0, nullptr, "GetCompletionEvent"},
24 {1, nullptr, "Cancel"},
25 {10100, nullptr, "GetFriendListIds"},
26 {10101, &IFriendService::GetFriendList, "GetFriendList"},
27 {10102, nullptr, "UpdateFriendInfo"},
28 {10110, nullptr, "GetFriendProfileImage"},
29 {10120, nullptr, "Unknown10120"},
30 {10121, nullptr, "Unknown10121"},
31 {10200, nullptr, "SendFriendRequestForApplication"},
32 {10211, nullptr, "AddFacedFriendRequestForApplication"},
33 {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"},
34 {10420, nullptr, "Unknown10420"},
35 {10421, nullptr, "Unknown10421"},
36 {10500, nullptr, "GetProfileList"},
37 {10600, nullptr, "DeclareOpenOnlinePlaySession"},
38 {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"},
39 {10610, &IFriendService::UpdateUserPresence, "UpdateUserPresence"},
40 {10700, nullptr, "GetPlayHistoryRegistrationKey"},
41 {10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"},
42 {10702, nullptr, "AddPlayHistory"},
43 {11000, nullptr, "GetProfileImageUrl"},
44 {20100, nullptr, "GetFriendCount"},
45 {20101, nullptr, "GetNewlyFriendCount"},
46 {20102, nullptr, "GetFriendDetailedInfo"},
47 {20103, nullptr, "SyncFriendList"},
48 {20104, nullptr, "RequestSyncFriendList"},
49 {20110, nullptr, "LoadFriendSetting"},
50 {20200, nullptr, "GetReceivedFriendRequestCount"},
51 {20201, nullptr, "GetFriendRequestList"},
52 {20300, nullptr, "GetFriendCandidateList"},
53 {20301, nullptr, "GetNintendoNetworkIdInfo"},
54 {20302, nullptr, "GetSnsAccountLinkage"},
55 {20303, nullptr, "GetSnsAccountProfile"},
56 {20304, nullptr, "GetSnsAccountFriendList"},
57 {20400, nullptr, "GetBlockedUserList"},
58 {20401, nullptr, "SyncBlockedUserList"},
59 {20500, nullptr, "GetProfileExtraList"},
60 {20501, nullptr, "GetRelationship"},
61 {20600, nullptr, "GetUserPresenceView"},
62 {20700, nullptr, "GetPlayHistoryList"},
63 {20701, nullptr, "GetPlayHistoryStatistics"},
64 {20800, nullptr, "LoadUserSetting"},
65 {20801, nullptr, "SyncUserSetting"},
66 {20900, nullptr, "RequestListSummaryOverlayNotification"},
67 {21000, nullptr, "GetExternalApplicationCatalog"},
68 {22000, nullptr, "GetReceivedFriendInvitationList"},
69 {22001, nullptr, "GetReceivedFriendInvitationDetailedInfo"},
70 {22010, nullptr, "GetReceivedFriendInvitationCountCache"},
71 {30100, nullptr, "DropFriendNewlyFlags"},
72 {30101, nullptr, "DeleteFriend"},
73 {30110, nullptr, "DropFriendNewlyFlag"},
74 {30120, nullptr, "ChangeFriendFavoriteFlag"},
75 {30121, nullptr, "ChangeFriendOnlineNotificationFlag"},
76 {30200, nullptr, "SendFriendRequest"},
77 {30201, nullptr, "SendFriendRequestWithApplicationInfo"},
78 {30202, nullptr, "CancelFriendRequest"},
79 {30203, nullptr, "AcceptFriendRequest"},
80 {30204, nullptr, "RejectFriendRequest"},
81 {30205, nullptr, "ReadFriendRequest"},
82 {30210, nullptr, "GetFacedFriendRequestRegistrationKey"},
83 {30211, nullptr, "AddFacedFriendRequest"},
84 {30212, nullptr, "CancelFacedFriendRequest"},
85 {30213, nullptr, "GetFacedFriendRequestProfileImage"},
86 {30214, nullptr, "GetFacedFriendRequestProfileImageFromPath"},
87 {30215, nullptr, "SendFriendRequestWithExternalApplicationCatalogId"},
88 {30216, nullptr, "ResendFacedFriendRequest"},
89 {30217, nullptr, "SendFriendRequestWithNintendoNetworkIdInfo"},
90 {30300, nullptr, "GetSnsAccountLinkPageUrl"},
91 {30301, nullptr, "UnlinkSnsAccount"},
92 {30400, nullptr, "BlockUser"},
93 {30401, nullptr, "BlockUserWithApplicationInfo"},
94 {30402, nullptr, "UnblockUser"},
95 {30500, nullptr, "GetProfileExtraFromFriendCode"},
96 {30700, nullptr, "DeletePlayHistory"},
97 {30810, nullptr, "ChangePresencePermission"},
98 {30811, nullptr, "ChangeFriendRequestReception"},
99 {30812, nullptr, "ChangePlayLogPermission"},
100 {30820, nullptr, "IssueFriendCode"},
101 {30830, nullptr, "ClearPlayLog"},
102 {30900, nullptr, "SendFriendInvitation"},
103 {30910, nullptr, "ReadFriendInvitation"},
104 {30911, nullptr, "ReadAllFriendInvitations"},
105 {40100, nullptr, "Unknown40100"},
106 {40400, nullptr, "Unknown40400"},
107 {49900, nullptr, "DeleteNetworkServiceAccountCache"},
108 };
109 // clang-format on
110
111 RegisterHandlers(functions);
112 }
113
114 private:
115 enum class PresenceFilter : u32 {
116 None = 0,
117 Online = 1,
118 OnlinePlay = 2,
119 OnlineOrOnlinePlay = 3,
120 };
121
122 struct SizedFriendFilter {
123 PresenceFilter presence;
124 u8 is_favorite;
125 u8 same_app;
126 u8 same_app_played;
127 u8 arbitary_app_played;
128 u64 group_id;
129 };
130 static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size");
131
GetBlockedUserListIds(Kernel::HLERequestContext & ctx)132 void GetBlockedUserListIds(Kernel::HLERequestContext& ctx) {
133 // This is safe to stub, as there should be no adverse consequences from reporting no
134 // blocked users.
135 LOG_WARNING(Service_ACC, "(STUBBED) called");
136 IPC::ResponseBuilder rb{ctx, 3};
137 rb.Push(RESULT_SUCCESS);
138 rb.Push<u32>(0); // Indicates there are no blocked users
139 }
140
DeclareCloseOnlinePlaySession(Kernel::HLERequestContext & ctx)141 void DeclareCloseOnlinePlaySession(Kernel::HLERequestContext& ctx) {
142 // Stub used by Splatoon 2
143 LOG_WARNING(Service_ACC, "(STUBBED) called");
144 IPC::ResponseBuilder rb{ctx, 2};
145 rb.Push(RESULT_SUCCESS);
146 }
147
UpdateUserPresence(Kernel::HLERequestContext & ctx)148 void UpdateUserPresence(Kernel::HLERequestContext& ctx) {
149 // Stub used by Retro City Rampage
150 LOG_WARNING(Service_ACC, "(STUBBED) called");
151 IPC::ResponseBuilder rb{ctx, 2};
152 rb.Push(RESULT_SUCCESS);
153 }
154
GetFriendList(Kernel::HLERequestContext & ctx)155 void GetFriendList(Kernel::HLERequestContext& ctx) {
156 IPC::RequestParser rp{ctx};
157 const auto friend_offset = rp.Pop<u32>();
158 const auto uuid = rp.PopRaw<Common::UUID>();
159 [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>();
160 const auto pid = rp.Pop<u64>();
161 LOG_WARNING(Service_ACC, "(STUBBED) called, offset={}, uuid={}, pid={}", friend_offset,
162 uuid.Format(), pid);
163
164 IPC::ResponseBuilder rb{ctx, 3};
165 rb.Push(RESULT_SUCCESS);
166
167 rb.Push<u32>(0); // Friend count
168 // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId"
169 }
170 };
171
172 class INotificationService final : public ServiceFramework<INotificationService> {
173 public:
INotificationService(Common::UUID uuid_,Core::System & system_)174 explicit INotificationService(Common::UUID uuid_, Core::System& system_)
175 : ServiceFramework{system_, "INotificationService"}, uuid{uuid_} {
176 // clang-format off
177 static const FunctionInfo functions[] = {
178 {0, &INotificationService::GetEvent, "GetEvent"},
179 {1, &INotificationService::Clear, "Clear"},
180 {2, &INotificationService::Pop, "Pop"}
181 };
182 // clang-format on
183
184 RegisterHandlers(functions);
185
186 notification_event = Kernel::WritableEvent::CreateEventPair(
187 system.Kernel(), "INotificationService:NotifyEvent");
188 }
189
190 private:
GetEvent(Kernel::HLERequestContext & ctx)191 void GetEvent(Kernel::HLERequestContext& ctx) {
192 LOG_DEBUG(Service_ACC, "called");
193
194 IPC::ResponseBuilder rb{ctx, 2, 1};
195 rb.Push(RESULT_SUCCESS);
196 rb.PushCopyObjects(notification_event.readable);
197 }
198
Clear(Kernel::HLERequestContext & ctx)199 void Clear(Kernel::HLERequestContext& ctx) {
200 LOG_DEBUG(Service_ACC, "called");
201 while (!notifications.empty()) {
202 notifications.pop();
203 }
204 std::memset(&states, 0, sizeof(States));
205
206 IPC::ResponseBuilder rb{ctx, 2};
207 rb.Push(RESULT_SUCCESS);
208 }
209
Pop(Kernel::HLERequestContext & ctx)210 void Pop(Kernel::HLERequestContext& ctx) {
211 LOG_DEBUG(Service_ACC, "called");
212
213 if (notifications.empty()) {
214 LOG_ERROR(Service_ACC, "No notifications in queue!");
215 IPC::ResponseBuilder rb{ctx, 2};
216 rb.Push(ERR_NO_NOTIFICATIONS);
217 return;
218 }
219
220 const auto notification = notifications.front();
221 notifications.pop();
222
223 switch (notification.notification_type) {
224 case NotificationTypes::HasUpdatedFriendsList:
225 states.has_updated_friends = false;
226 break;
227 case NotificationTypes::HasReceivedFriendRequest:
228 states.has_received_friend_request = false;
229 break;
230 default:
231 // HOS seems not have an error case for an unknown notification
232 LOG_WARNING(Service_ACC, "Unknown notification {:08X}", notification.notification_type);
233 break;
234 }
235
236 IPC::ResponseBuilder rb{ctx, 6};
237 rb.Push(RESULT_SUCCESS);
238 rb.PushRaw<SizedNotificationInfo>(notification);
239 }
240
241 enum class NotificationTypes : u32 {
242 HasUpdatedFriendsList = 0x65,
243 HasReceivedFriendRequest = 0x1
244 };
245
246 struct SizedNotificationInfo {
247 NotificationTypes notification_type;
248 INSERT_PADDING_WORDS(
249 1); // TODO(ogniK): This doesn't seem to be used within any IPC returns as of now
250 u64_le account_id;
251 };
252 static_assert(sizeof(SizedNotificationInfo) == 0x10,
253 "SizedNotificationInfo is an incorrect size");
254
255 struct States {
256 bool has_updated_friends;
257 bool has_received_friend_request;
258 };
259
260 Common::UUID uuid{Common::INVALID_UUID};
261 Kernel::EventPair notification_event;
262 std::queue<SizedNotificationInfo> notifications;
263 States states{};
264 };
265
CreateFriendService(Kernel::HLERequestContext & ctx)266 void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) {
267 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
268 rb.Push(RESULT_SUCCESS);
269 rb.PushIpcInterface<IFriendService>(system);
270 LOG_DEBUG(Service_ACC, "called");
271 }
272
CreateNotificationService(Kernel::HLERequestContext & ctx)273 void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx) {
274 IPC::RequestParser rp{ctx};
275 auto uuid = rp.PopRaw<Common::UUID>();
276
277 LOG_DEBUG(Service_ACC, "called, uuid={}", uuid.Format());
278
279 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
280 rb.Push(RESULT_SUCCESS);
281 rb.PushIpcInterface<INotificationService>(uuid, system);
282 }
283
Interface(std::shared_ptr<Module> module_,Core::System & system_,const char * name)284 Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
285 const char* name)
286 : ServiceFramework{system_, name}, module{std::move(module_)} {}
287
288 Module::Interface::~Interface() = default;
289
InstallInterfaces(SM::ServiceManager & service_manager,Core::System & system)290 void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
291 auto module = std::make_shared<Module>();
292 std::make_shared<Friend>(module, system, "friend:a")->InstallAsService(service_manager);
293 std::make_shared<Friend>(module, system, "friend:m")->InstallAsService(service_manager);
294 std::make_shared<Friend>(module, system, "friend:s")->InstallAsService(service_manager);
295 std::make_shared<Friend>(module, system, "friend:u")->InstallAsService(service_manager);
296 std::make_shared<Friend>(module, system, "friend:v")->InstallAsService(service_manager);
297 }
298
299 } // namespace Service::Friend
300