1 // Copyright (c) 2013- PPSSPP Project.
2
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
11
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18 #if defined(_WIN32)
19 #include <WinSock2.h>
20 #include "Common/CommonWindows.h"
21 #endif
22
23 #if !defined(_WIN32)
24 #include <netinet/tcp.h>
25 #endif
26
27 #ifndef MSG_NOSIGNAL
28 // Default value to 0x00 (do nothing) in systems where it's not supported.
29 #define MSG_NOSIGNAL 0x00
30 #endif
31
32 #include <mutex>
33 #include "Common/Thread/ThreadUtil.h"
34 // sceNetAdhoc
35
36 // This is a direct port of Coldbird's code from http://code.google.com/p/aemu/
37 // All credit goes to him!
38 #include "Core/Core.h"
39 #include "Core/Host.h"
40 #include "Core/Reporting.h"
41 #include "Core/MemMapHelpers.h"
42 #include "Common/Serialize/Serializer.h"
43 #include "Common/Serialize/SerializeFuncs.h"
44 #include "Common/Serialize/SerializeMap.h"
45 #include "Common/TimeUtil.h"
46 #include "Core/MIPS/MIPSCodeUtils.h"
47 #include "Core/Util/PortManager.h"
48
49 #include "Core/HLE/HLEHelperThread.h"
50 #include "Core/HLE/FunctionWrappers.h"
51 #include "Core/HLE/sceKernelThread.h"
52 #include "Core/HLE/sceKernel.h"
53 #include "Core/HLE/sceKernelMemory.h"
54 #include "Core/HLE/sceKernelModule.h"
55 #include "Core/HLE/sceKernelInterrupt.h"
56 #include "Core/HLE/sceNetAdhoc.h"
57 #include "Core/HLE/sceNet.h"
58 #include "Core/HLE/proAdhocServer.h"
59 #include "Core/HLE/KernelWaitHelpers.h"
60 #include "Common/Data/Text/I18n.h"
61
62
63 // shared in sceNetAdhoc.h since it need to be used from sceNet.cpp also
64 // TODO: Make accessor functions instead, and throw all this state in a struct.
65 bool netAdhocInited;
66 bool netAdhocctlInited;
67 bool networkInited = false;
68
69 #define DISCOVER_DURATION_US 2000000 // 2 seconds is probably the normal time it takes for PSP to connect to a group (ie. similar to NetconfigDialog time)
70 u64 netAdhocDiscoverStartTime = 0;
71 s32 netAdhocDiscoverStatus = NET_ADHOC_DISCOVER_STATUS_NONE;
72 bool netAdhocDiscoverIsStopping = false;
73 SceNetAdhocDiscoverParam* netAdhocDiscoverParam = nullptr;
74 u32 netAdhocDiscoverBufAddr = 0;
75
76 bool netAdhocGameModeEntered = false;
77 int netAdhocEnterGameModeTimeout = 15000000; // 15 sec as default timeout, to wait for all players to join
78
79 bool netAdhocMatchingInited;
80 int netAdhocMatchingStarted = 0;
81 int adhocDefaultTimeout = 5000000; //2000000 usec // For some unknown reason, sometimes it tooks more than 2 seconds for Adhocctl Init to connect to AdhocServer on localhost (normally only 10 ms), and sometimes it tooks more than 1 seconds for built-in AdhocServer to be ready (normally only 1 ms)
82 int adhocDefaultDelay = 10000; //10000
83 int adhocExtraDelay = 20000; //20000
84 int adhocEventPollDelay = 100000; //100000; // Same timings with PSP_ADHOCCTL_RECV_TIMEOUT ?
85 int adhocMatchingEventDelay = 30000; //30000
86 int adhocEventDelay = 2000000; //2000000 on real PSP ?
87 u32 defaultLastRecvDelta = 10000; //10000 usec worked well for games published by Falcom (ie. Ys vs Sora Kiseki, Vantage Master Portable)
88
89 SceUID threadAdhocID;
90
91 std::recursive_mutex adhocEvtMtx;
92 std::deque<std::pair<u32, u32>> adhocctlEvents;
93 std::deque<MatchingArgs> matchingEvents;
94 std::map<int, AdhocctlHandler> adhocctlHandlers;
95 std::vector<SceUID> matchingThreads;
96 int IsAdhocctlInCB = 0;
97
98 int adhocctlNotifyEvent = -1;
99 int adhocctlStateEvent = -1;
100 int adhocSocketNotifyEvent = -1;
101 std::map<int, AdhocctlRequest> adhocctlRequests;
102 std::map<u64, AdhocSocketRequest> adhocSocketRequests;
103 std::map<u64, AdhocSendTargets> sendTargetPeers;
104
105 int gameModeNotifyEvent = -1;
106
107 u32 dummyThreadHackAddr = 0;
108 u32_le dummyThreadCode[3];
109 u32 matchingThreadHackAddr = 0;
110 u32_le matchingThreadCode[3];
111
112 int matchingEventThread(int matchingId);
113 int matchingInputThread(int matchingId);
114 void sendBulkDataPacket(SceNetAdhocMatchingContext* context, SceNetEtherAddr* mac, int datalen, void* data);
115 int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEtherAddr* addr, u16_le* port);
116 int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout, int nonblock);
117 int FlushPtpSocket(int socketId);
118 int NetAdhocGameMode_DeleteMaster();
119 int NetAdhocctl_ExitGameMode();
120 int NetAdhocPtp_Connect(int id, int timeout, int flag, bool allowForcedConnect = true);
121 static int sceNetAdhocPdpCreate(const char* mac, int port, int bufferSize, u32 flag);
122 static int sceNetAdhocPdpSend(int id, const char* mac, u32 port, void* data, int len, int timeout, int flag);
123 static int sceNetAdhocPdpRecv(int id, void* addr, void* port, void* buf, void* dataLength, u32 timeout, int flag);
124
125
__NetAdhocShutdown()126 void __NetAdhocShutdown() {
127 // Kill AdhocServer Thread
128 adhocServerRunning = false;
129 if (adhocServerThread.joinable()) {
130 adhocServerThread.join();
131 }
132
133 // Checks to avoid confusing logspam
134 if (netAdhocMatchingInited) {
135 NetAdhocMatching_Term();
136 }
137 if (netAdhocctlInited) {
138 NetAdhocctl_Term();
139 }
140 if (netAdhocInited) {
141 NetAdhoc_Term();
142 }
143 if (dummyThreadHackAddr) {
144 kernelMemory.Free(dummyThreadHackAddr);
145 dummyThreadHackAddr = 0;
146 }
147 if (matchingThreadHackAddr) {
148 kernelMemory.Free(matchingThreadHackAddr);
149 matchingThreadHackAddr = 0;
150 }
151 }
152
IsGameModeActive()153 bool IsGameModeActive() {
154 return netAdhocGameModeEntered;
155 }
156
__GameModeNotify(u64 userdata,int cyclesLate)157 static void __GameModeNotify(u64 userdata, int cyclesLate) {
158 SceUID threadID = userdata >> 32;
159
160 if (IsGameModeActive()) {
161 // Need to make sure all replicas have been created before we start syncing data
162 if (replicaGameModeAreas.size() == (gameModeMacs.size() - 1)) {
163 // Socket's buffer size should fit the largest size from master/replicas, so we waited until Master & all Replicas to be created first before creating the socket, since there are games (ie. Fading Shadows) that use different buffer size for Master and Replica.
164 if (gameModeSocket < 0 && !isZeroMAC(&masterGameModeArea.mac)) {
165 u8* buf = (u8*)realloc(gameModeBuffer, gameModeBuffSize);
166 if (buf)
167 gameModeBuffer = buf;
168
169 if ((gameModeSocket = sceNetAdhocPdpCreate((const char*)&masterGameModeArea.mac, ADHOC_GAMEMODE_PORT, gameModeBuffSize, 0)) < 0) {
170 ERROR_LOG(SCENET, "GameMode: Failed to create socket (Error %08x)", gameModeSocket);
171 __KernelResumeThreadFromWait(threadID, gameModeSocket);
172 return;
173 }
174 else
175 INFO_LOG(SCENET, "GameMode: Synchronizer (%d, %d) has started", gameModeSocket, gameModeBuffSize);
176 }
177 if (gameModeSocket < 0) {
178 // ReSchedule
179 CoreTiming::ScheduleEvent(usToCycles(GAMEMODE_UPDATE_INTERVAL) - cyclesLate, gameModeNotifyEvent, userdata);
180 return;
181 }
182 auto sock = adhocSockets[gameModeSocket - 1];
183 if (!sock) {
184 WARN_LOG(SCENET, "GameMode: Socket (%d) got deleted", gameModeSocket);
185 return;
186 }
187
188 // Send Master data
189 if (masterGameModeArea.dataUpdated) {
190 int sentcount = 0;
191 for (auto& gma : replicaGameModeAreas) {
192 if (!gma.dataSent && IsSocketReady(sock->data.pdp.id, false, true) > 0) {
193 u16_le port = ADHOC_GAMEMODE_PORT;
194 auto it = gameModePeerPorts.find(gma.mac);
195 if (it != gameModePeerPorts.end())
196 port = it->second;
197
198 int sent = sceNetAdhocPdpSend(gameModeSocket, (const char*)&gma.mac, port, masterGameModeArea.data, masterGameModeArea.size, 0, ADHOC_F_NONBLOCK);
199 if (sent != ERROR_NET_ADHOC_WOULD_BLOCK) {
200 gma.dataSent = 1;
201 DEBUG_LOG(SCENET, "GameMode: Master data Sent %d bytes to Area #%d [%s]", masterGameModeArea.size, gma.id, mac2str(&gma.mac).c_str());
202 sentcount++;
203 }
204 }
205 else if (gma.dataSent) sentcount++;
206 }
207 if (sentcount == replicaGameModeAreas.size())
208 masterGameModeArea.dataUpdated = 0;
209 }
210 // Need to sync (send + recv) all players initial data (data from CreateMaster) after Master + All Replicas are created, and before the first UpdateMaster / UpdateReplica is called for Star Wars The Force Unleashed to show the correct players color on minimap (also prevent Starting issue on other GameMode games)
211 else {
212 u32 error;
213 SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error);
214 if (error == 0 && waitID == GAMEMODE_WAITID) {
215 // Resume thread after all replicas data have been received
216 int recvd = 0;
217 for (auto& gma : replicaGameModeAreas) {
218 // Either replicas new data has been received or that player has been disconnected
219 if (gma.dataUpdated || gma.updateTimestamp == 0) {
220 recvd++;
221 // Since we're able to receive data, now we're certain that remote player is listening and ready to receive data, so we send initial data one more time in case they're not listening yet on previous attempt (ie. Pocket Pool)
222 if (gma.dataUpdated) {
223 u16_le port = ADHOC_GAMEMODE_PORT;
224 auto it = gameModePeerPorts.find(gma.mac);
225 if (it != gameModePeerPorts.end())
226 port = it->second;
227
228 sceNetAdhocPdpSend(gameModeSocket, (const char*)&gma.mac, port, masterGameModeArea.data, masterGameModeArea.size, 0, ADHOC_F_NONBLOCK);
229 }
230 }
231 }
232 // Resume blocked thread
233 u64 now = CoreTiming::GetGlobalTimeUsScaled();
234 if (recvd == replicaGameModeAreas.size()) {
235 u32 waitVal = __KernelGetWaitValue(threadID, error);
236 if (error == 0) {
237 DEBUG_LOG(SCENET, "GameMode: Resuming Thread %d after Master data Synced (Result = %08x)", threadID, waitVal);
238 __KernelResumeThreadFromWait(threadID, waitVal);
239 }
240 else
241 ERROR_LOG(SCENET, "GameMode: Error (%08x) on WaitValue %d ThreadID %d", error, waitVal, threadID);
242 }
243 // Attempt to Re-Send initial Master data (in case previous packets were lost)
244 else if (static_cast<s64>(now - masterGameModeArea.updateTimestamp) > GAMEMODE_SYNC_TIMEOUT) {
245 DEBUG_LOG(SCENET, "GameMode: Attempt to Re-Send Master data after Sync Timeout (%d us)", GAMEMODE_SYNC_TIMEOUT);
246 // Reset Sent marker on players who haven't replied yet (except disconnected players)
247 for (auto& gma : replicaGameModeAreas)
248 if (!gma.dataUpdated && gma.updateTimestamp != 0)
249 gma.dataSent = 0;
250 masterGameModeArea.updateTimestamp = now;
251 masterGameModeArea.dataUpdated = 1;
252 }
253 }
254 }
255
256 // Recv new Replica data when available
257 if (IsSocketReady(sock->data.pdp.id, true, false) > 0) {
258 SceNetEtherAddr sendermac;
259 s32_le senderport = ADHOC_GAMEMODE_PORT;
260 s32_le bufsz = gameModeBuffSize;
261 int ret = sceNetAdhocPdpRecv(gameModeSocket, &sendermac, &senderport, gameModeBuffer, &bufsz, 0, ADHOC_F_NONBLOCK);
262 if (ret >= 0 && bufsz > 0) {
263 // Shows a warning if the sender/source port is different than what it supposed to be.
264 if (senderport != ADHOC_GAMEMODE_PORT && senderport != gameModePeerPorts[sendermac]) {
265 char name[9] = {};
266 auto n = GetI18NCategory("Networking");
267 peerlock.lock();
268 SceNetAdhocctlPeerInfo* peer = findFriend(&sendermac);
269 if (peer != NULL)
270 truncate_cpy(name, sizeof(name), (const char*)peer->nickname.data);
271 WARN_LOG(SCENET, "GameMode: Unknown Source Port from [%s][%s:%u -> %u] (Result=%i, Size=%i)", name, mac2str(&sendermac).c_str(), senderport, ADHOC_GAMEMODE_PORT, ret, bufsz);
272 host->NotifyUserMessage(std::string(n->T("GM: Data from Unknown Port")) + std::string(" [") + std::string(name) + std::string("]:") + std::to_string(senderport) + std::string(" -> ") + std::to_string(ADHOC_GAMEMODE_PORT) + std::string(" (") + std::to_string(portOffset) + std::string(")"), 2.0, 0x0080ff);
273 peerlock.unlock();
274 }
275 // Keeping track of the source port for further communication, in case it was re-mapped by router or ISP for some reason.
276 gameModePeerPorts[sendermac] = senderport;
277
278 for (auto& gma : replicaGameModeAreas) {
279 if (IsMatch(gma.mac, sendermac)) {
280 DEBUG_LOG(SCENET, "GameMode: Replica data Received %d bytes for Area #%d [%s]", bufsz, gma.id, mac2str(&sendermac).c_str());
281 memcpy(gma.data, gameModeBuffer, std::min(gma.size, bufsz));
282 gma.dataUpdated = 1;
283 gma.updateTimestamp = CoreTiming::GetGlobalTimeUsScaled();
284 break;
285 }
286 }
287 }
288 }
289 }
290
291 // ReSchedule
292 CoreTiming::ScheduleEvent(usToCycles(GAMEMODE_UPDATE_INTERVAL) - cyclesLate, gameModeNotifyEvent, userdata);
293 return;
294 }
295 INFO_LOG(SCENET, "GameMode Scheduler (%d, %d) has ended", gameModeSocket, gameModeBuffSize);
296 }
297
__AdhocctlNotify(u64 userdata,int cyclesLate)298 static void __AdhocctlNotify(u64 userdata, int cyclesLate) {
299 SceUID threadID = userdata >> 32;
300 int uid = (int)(userdata & 0xFFFFFFFF);
301
302 s64 result = 0;
303 u32 error = 0;
304
305 SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error);
306 if (waitID == 0 || error != 0) {
307 WARN_LOG(SCENET, "sceNetAdhocctl Socket WaitID(%i) on Thread(%i) already woken up? (error: %08x)", uid, threadID, error);
308 return;
309 }
310
311 // Socket not found?! Should never happen! But if it ever happened (ie. loaded from SaveState where adhocctlRequests got cleared) return BUSY and let the game try again.
312 if (adhocctlRequests.find(uid) == adhocctlRequests.end()) {
313 WARN_LOG(SCENET, "sceNetAdhocctl Socket WaitID(%i) not found!", uid);
314 __KernelResumeThreadFromWait(threadID, ERROR_NET_ADHOCCTL_BUSY);
315 return;
316 }
317
318 AdhocctlRequest& req = adhocctlRequests[uid];
319 int len = 0;
320
321 SceNetAdhocctlConnectPacketC2S packet;
322 memset(&packet, 0, sizeof(packet));
323 packet.base.opcode = req.opcode;
324 packet.group = req.group;
325
326 // Don't send any packets not in these cases (by setting the len to 0)
327 switch (req.opcode)
328 {
329 case OPCODE_CONNECT:
330 len = sizeof(packet);
331 break;
332 case OPCODE_SCAN:
333 case OPCODE_DISCONNECT:
334 len = 1;
335 break;
336 }
337
338 if (g_Config.bEnableWlan) {
339 // Send Packet if it wasn't succesfully sent before
340 int ret = 0;
341 int sockerr = 0;
342 if (len > 0) {
343 ret = SOCKET_ERROR;
344 sockerr = EAGAIN;
345 // Don't send anything yet if connection to Adhoc Server is still in progress
346 if (!isAdhocctlNeedLogin && IsSocketReady((int)metasocket, false, true) > 0) {
347 ret = send((int)metasocket, (const char*)&packet, len, MSG_NOSIGNAL);
348 sockerr = errno;
349 // Successfully Sent or Connection has been closed or Connection failure occurred
350 if (ret >= 0 || (ret == SOCKET_ERROR && sockerr != EAGAIN && sockerr != EWOULDBLOCK)) {
351 // Prevent from sending again
352 req.opcode = 0;
353 if (ret == SOCKET_ERROR)
354 DEBUG_LOG(SCENET, "sceNetAdhocctl[%i]: Socket Error (%i)", uid, sockerr);
355 }
356 }
357 }
358
359 // Retry until successfully sent. Login packet sent after successfully connected to Adhoc Server (indicated by networkInited), so we're not sending Login again here
360 if ((req.opcode == OPCODE_LOGIN && !networkInited) || (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK))) {
361 u64 now = (u64)(time_now_d() * 1000000.0);
362 if (now - adhocctlStartTime <= static_cast<u64>(adhocDefaultTimeout) + 500) {
363 // Try again in another 0.5ms until timedout.
364 CoreTiming::ScheduleEvent(usToCycles(500) - cyclesLate, adhocctlNotifyEvent, userdata);
365 return;
366 }
367 else if (req.opcode != OPCODE_LOGIN)
368 result = ERROR_NET_ADHOCCTL_BUSY;
369 }
370 }
371 else
372 result = ERROR_NET_ADHOCCTL_WLAN_SWITCH_OFF;
373
374 u32 waitVal = __KernelGetWaitValue(threadID, error);
375 __KernelResumeThreadFromWait(threadID, result);
376 DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %08x) Result (%08x) of sceNetAdhocctl - Opcode: %d, State: %d", waitID, error, (int)result, waitVal, adhocctlState);
377
378 // We are done with this request
379 adhocctlRequests.erase(uid);
380 }
381
__AdhocctlState(u64 userdata,int cyclesLate)382 static void __AdhocctlState(u64 userdata, int cyclesLate) {
383 SceUID threadID = userdata >> 32;
384 int uid = (int)(userdata & 0xFFFFFFFF);
385 int event = uid - 1;
386
387 s64 result = 0;
388 u32 error = 0;
389
390 SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error);
391 if (waitID == 0 || error != 0) {
392 WARN_LOG(SCENET, "sceNetAdhocctl State WaitID(%i) on Thread(%i) already woken up? (error: %08x)", uid, threadID, error);
393 return;
394 }
395
396 u32 waitVal = __KernelGetWaitValue(threadID, error);
397 if (error == 0) {
398 adhocctlState = waitVal;
399 // FIXME: It seems Adhocctl is still busy within the Adhocctl Handler function (ie. during callbacks),
400 // so we should probably set isAdhocctlBusy to false after mispscall are fully executed (ie. in afterAction).
401 // But since Adhocctl Handler is optional, there might be cases where there are no handler thus no callback/mipcall being triggered,
402 // so we should probably need to set isAdhocctlBusy to false here too as a workaround (or may be there is internal handler by default?)
403 if (adhocctlHandlers.empty())
404 isAdhocctlBusy = false;
405 }
406
407 __KernelResumeThreadFromWait(threadID, result);
408 DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %08x) Result (%08x) of sceNetAdhocctl - Event: %d, State: %d", waitID, error, (int)result, event, adhocctlState);
409 }
410
411 // Used to simulate blocking on metasocket when send OP code to AdhocServer
WaitBlockingAdhocctlSocket(AdhocctlRequest request,int usec,const char * reason)412 int WaitBlockingAdhocctlSocket(AdhocctlRequest request, int usec, const char* reason) {
413 int uid = (metasocket <= 0) ? 1 : (int)metasocket;
414
415 if (adhocctlRequests.find(uid) != adhocctlRequests.end()) {
416 WARN_LOG(SCENET, "sceNetAdhocctl - WaitID[%d] already existed, Socket is busy!", uid);
417 return ERROR_NET_ADHOCCTL_BUSY;
418 }
419
420 u64 param = ((u64)__KernelGetCurThread()) << 32 | uid;
421 adhocctlStartTime = (u64)(time_now_d() * 1000000.0);
422 adhocctlRequests[uid] = request;
423 CoreTiming::ScheduleEvent(usToCycles(usec), adhocctlNotifyEvent, param);
424 __KernelWaitCurThread(WAITTYPE_NET, uid, request.opcode, 0, false, reason);
425
426 // Always returning a success when waiting for callback, since error code returned via callback?
427 return 0;
428 }
429
430 // Used to change Adhocctl State after a delay and before executing callback mipscall (since we don't have beforeAction)
ScheduleAdhocctlState(int event,int newState,int usec,const char * reason)431 int ScheduleAdhocctlState(int event, int newState, int usec, const char* reason) {
432 int uid = event + 1;
433
434 u64 param = ((u64)__KernelGetCurThread()) << 32 | uid;
435 CoreTiming::ScheduleEvent(usToCycles(usec), adhocctlStateEvent, param);
436 __KernelWaitCurThread(WAITTYPE_NET, uid, newState, 0, false, reason);
437
438 return 0;
439 }
440
StartGameModeScheduler()441 int StartGameModeScheduler() {
442 INFO_LOG(SCENET, "Initiating GameMode Scheduler");
443 u64 param = ((u64)__KernelGetCurThread()) << 32;
444 CoreTiming::ScheduleEvent(usToCycles(GAMEMODE_INIT_DELAY), gameModeNotifyEvent, param);
445 return 0;
446 }
447
DoBlockingPdpRecv(int uid,AdhocSocketRequest & req,s64 & result)448 int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) {
449 auto sock = adhocSockets[req.id - 1];
450 if (!sock) {
451 result = ERROR_NET_ADHOC_SOCKET_DELETED;
452 return 0;
453 }
454 if (sock->flags & ADHOC_F_ALERTRECV) {
455 result = ERROR_NET_ADHOC_SOCKET_ALERTED;
456 sock->alerted_flags |= ADHOC_F_ALERTRECV;
457 return 0;
458 }
459
460 struct sockaddr_in sin;
461 socklen_t sinlen = sizeof(sin);
462 memset(&sin, 0, sinlen);
463
464 // On Windows: MSG_TRUNC are not supported on recvfrom (socket error WSAEOPNOTSUPP), so we use dummy buffer as an alternative
465 int ret = recvfrom(uid, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
466 int sockerr = errno;
467 if (ret > 0 && *req.length > 0)
468 memcpy(req.buffer, dummyPeekBuf64k, std::min(ret, *req.length));
469
470 // Note: UDP must not be received partially, otherwise leftover data in socket's buffer will be discarded
471 if (ret >= 0 && ret <= *req.length) {
472 sinlen = sizeof(sin);
473 memset(&sin, 0, sinlen);
474 ret = recvfrom(uid, (char*)req.buffer, std::max(0, *req.length), MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
475 // UDP can also receives 0 data, while on TCP receiving 0 data = connection gracefully closed, but not sure whether PDP can send/recv 0 data or not tho
476 *req.length = 0;
477 if (ret >= 0) {
478 DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", req.id, getLocalPort(uid), ret, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
479
480 // Peer MAC
481 SceNetEtherAddr mac;
482
483 // Find Peer MAC
484 if (resolveIP(sin.sin_addr.s_addr, &mac)) {
485 // Provide Sender Information
486 *req.remoteMAC = mac;
487 *req.remotePort = ntohs(sin.sin_port) - portOffset;
488
489 // Save Length
490 *req.length = ret;
491
492 // Update last recv timestamp
493 peerlock.lock();
494 auto peer = findFriend(&mac);
495 if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
496 peerlock.unlock();
497 }
498 // Unknown Peer
499 else {
500 *req.length = ret;
501 *req.remotePort = ntohs(sin.sin_port) - portOffset;
502
503 WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %s:%u", req.id, getLocalPort(uid), ret, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
504 }
505 }
506 result = 0;
507 }
508 // On Windows: recvfrom on UDP can get error WSAECONNRESET when previous sendto's destination is unreachable (or destination port is not bound yet), may need to disable SIO_UDP_CONNRESET error
509 else if (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ECONNRESET) {
510 u64 now = (u64)(time_now_d() * 1000000.0);
511 if (req.timeout == 0 || now - req.startTime <= req.timeout) {
512 // Try again later
513 return -1;
514 }
515 else
516 result = ERROR_NET_ADHOC_TIMEOUT;
517 }
518 // Returning required buffer size when available data in recv buffer is larger than provided buffer size
519 else if (ret > *req.length) {
520 WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Peeked %u/%u bytes from %s:%u\n", req.id, getLocalPort(uid), ret, *req.length, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
521 *req.length = ret;
522
523 // Peer MAC
524 SceNetEtherAddr mac;
525
526 // Find Peer MAC
527 if (resolveIP(sin.sin_addr.s_addr, &mac)) {
528 // Provide Sender Information
529 *req.remoteMAC = mac;
530 *req.remotePort = ntohs(sin.sin_port) - portOffset;
531
532 // FIXME: Do we need to update last recv timestamp? eventhough data hasn't been retrieved yet (ie. peeked)
533 peerlock.lock();
534 auto peer = findFriend(&mac);
535 if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
536 peerlock.unlock();
537 }
538 result = ERROR_NET_ADHOC_NOT_ENOUGH_SPACE;
539 }
540 // FIXME: Blocking operation with infinite timeout(0) should never get a TIMEOUT error, right? May be we should return INVALID_ARG instead if it was infinite timeout (0)?
541 else
542 result = ERROR_NET_ADHOC_TIMEOUT; // ERROR_NET_ADHOC_INVALID_ARG; // ERROR_NET_ADHOC_DISCONNECTED
543
544 if (ret == SOCKET_ERROR)
545 DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i]: Socket Error (%i)", req.id, sockerr);
546
547 return 0;
548 }
549
DoBlockingPdpSend(int uid,AdhocSocketRequest & req,s64 & result,AdhocSendTargets & targetPeers)550 int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTargets& targetPeers) {
551 auto sock = adhocSockets[req.id - 1];
552 if (!sock) {
553 result = ERROR_NET_ADHOC_SOCKET_DELETED;
554 return 0;
555 }
556 auto& pdpsocket = sock->data.pdp;
557 if (sock->flags & ADHOC_F_ALERTSEND) {
558 result = ERROR_NET_ADHOC_SOCKET_ALERTED;
559 sock->alerted_flags |= ADHOC_F_ALERTSEND;
560 return 0;
561 }
562
563 result = 0;
564 bool retry = false;
565 for (auto peer = targetPeers.peers.begin(); peer != targetPeers.peers.end(); ) {
566 // Fill in Target Structure
567 struct sockaddr_in target;
568 target.sin_family = AF_INET;
569 target.sin_addr.s_addr = peer->ip;
570 target.sin_port = htons(peer->port + ((isOriPort && !isPrivateIP(peer->ip)) ? 0 : portOffset));
571
572 int ret = sendto(pdpsocket.id, (const char*)req.buffer, targetPeers.length, MSG_NOSIGNAL, (struct sockaddr*)&target, sizeof(target));
573 int sockerr = errno;
574
575 if (ret >= 0) {
576 DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](B): Sent %u bytes to %s:%u\n", uid, getLocalPort(pdpsocket.id), ret, ip2str(target.sin_addr).c_str(), ntohs(target.sin_port));
577 // Remove successfully sent to peer to prevent sending the same data again during a retry
578 peer = targetPeers.peers.erase(peer);
579 }
580 else {
581 if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK)) {
582 u64 now = (u64)(time_now_d() * 1000000.0);
583 if (req.timeout == 0 || now - req.startTime <= req.timeout) {
584 retry = true;
585 }
586 else
587 // FIXME: Does Broadcast always success? even with timeout/blocking?
588 result = ERROR_NET_ADHOC_TIMEOUT;
589 }
590 ++peer;
591 }
592
593 if (ret == SOCKET_ERROR)
594 DEBUG_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend[%i:%u->%u](B) [size=%i]", sockerr, uid, getLocalPort(pdpsocket.id), ntohs(target.sin_port), targetPeers.length);
595 }
596
597 if (retry)
598 return -1;
599
600 return 0;
601 }
602
DoBlockingPtpSend(int uid,AdhocSocketRequest & req,s64 & result)603 int DoBlockingPtpSend(int uid, AdhocSocketRequest& req, s64& result) {
604 auto sock = adhocSockets[req.id - 1];
605 if (!sock) {
606 result = ERROR_NET_ADHOC_SOCKET_DELETED;
607 return 0;
608 }
609 auto& ptpsocket = sock->data.ptp;
610 if (sock->flags & ADHOC_F_ALERTSEND) {
611 result = ERROR_NET_ADHOC_SOCKET_ALERTED;
612 sock->alerted_flags |= ADHOC_F_ALERTSEND;
613 return 0;
614 }
615
616 // Send Data
617 int ret = send(uid, (const char*)req.buffer, *req.length, MSG_NOSIGNAL);
618 int sockerr = errno;
619
620 // Success
621 if (ret > 0) {
622 // Save Length
623 *req.length = ret;
624
625 DEBUG_LOG(SCENET, "sceNetAdhocPtpSend[%i:%u]: Sent %u bytes to %s:%u\n", req.id, ptpsocket.lport, ret, mac2str(&ptpsocket.paddr).c_str(), ptpsocket.pport);
626
627 // Set to Established on successful Send when an attempt to Connect was initiated
628 if (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT)
629 ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED;
630
631 // Return Success
632 result = 0;
633 }
634 else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK || (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT && (sockerr == ENOTCONN || connectInProgress(sockerr))))) {
635 u64 now = (u64)(time_now_d() * 1000000.0);
636 if (req.timeout == 0 || now - req.startTime <= req.timeout) {
637 return -1;
638 }
639 else
640 result = ERROR_NET_ADHOC_TIMEOUT;
641 }
642 else {
643 // Change Socket State. // FIXME: Does Alerted Socket should be closed too?
644 ptpsocket.state = ADHOC_PTP_STATE_CLOSED;
645
646 // Disconnected
647 result = ERROR_NET_ADHOC_DISCONNECTED;
648 }
649
650 if (ret == SOCKET_ERROR)
651 DEBUG_LOG(SCENET, "sceNetAdhocPtpSend[%i]: Socket Error (%i)", req.id, sockerr);
652
653 return 0;
654 }
655
DoBlockingPtpRecv(int uid,AdhocSocketRequest & req,s64 & result)656 int DoBlockingPtpRecv(int uid, AdhocSocketRequest& req, s64& result) {
657 auto sock = adhocSockets[req.id - 1];
658 if (!sock) {
659 result = ERROR_NET_ADHOC_SOCKET_DELETED;
660 return 0;
661 }
662 auto& ptpsocket = sock->data.ptp;
663 if (sock->flags & ADHOC_F_ALERTRECV) {
664 result = ERROR_NET_ADHOC_SOCKET_ALERTED;
665 sock->alerted_flags |= ADHOC_F_ALERTRECV;
666 return 0;
667 }
668
669 int ret = recv(uid, (char*)req.buffer, std::max(0, *req.length), MSG_NOSIGNAL);
670 int sockerr = errno;
671
672 // Received Data. POSIX: May received 0 bytes when the remote peer already closed the connection.
673 if (ret > 0) {
674 DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv[%i:%u]: Received %u bytes from %s:%u\n", req.id, ptpsocket.lport, ret, mac2str(&ptpsocket.paddr).c_str(), ptpsocket.pport);
675 // Save Length
676 *req.length = ret;
677
678 // Update last recv timestamp
679 peerlock.lock();
680 auto peer = findFriend(&ptpsocket.paddr);
681 if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
682 peerlock.unlock();
683
684 // Set to Established on successful Recv when an attempt to Connect was initiated
685 if (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT)
686 ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED;
687
688 result = 0;
689 }
690 else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK || (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT && (sockerr == ENOTCONN || connectInProgress(sockerr))))) {
691 u64 now = (u64)(time_now_d() * 1000000.0);
692 if (req.timeout == 0 || now - req.startTime <= req.timeout) {
693 return -1;
694 }
695 else
696 result = ERROR_NET_ADHOC_TIMEOUT;
697 }
698 else {
699 // Change Socket State. // FIXME: Does Alerted Socket should be closed too?
700 ptpsocket.state = ADHOC_PTP_STATE_CLOSED;
701
702 // Disconnected
703 result = ERROR_NET_ADHOC_DISCONNECTED; // ERROR_NET_ADHOC_INVALID_ARG
704 }
705
706 if (ret == SOCKET_ERROR)
707 DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv[%i]: Socket Error (%i)", req.id, sockerr);
708
709 return 0;
710 }
711
DoBlockingPtpAccept(int uid,AdhocSocketRequest & req,s64 & result)712 int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) {
713 auto sock = adhocSockets[req.id - 1];
714 if (!sock) {
715 result = ERROR_NET_ADHOC_SOCKET_DELETED;
716 return 0;
717 }
718 auto& ptpsocket = sock->data.ptp;
719 if (sock->flags & ADHOC_F_ALERTACCEPT) {
720 result = ERROR_NET_ADHOC_SOCKET_ALERTED;
721 sock->alerted_flags |= ADHOC_F_ALERTACCEPT;
722 return 0;
723 }
724
725 struct sockaddr_in sin;
726 memset(&sin, 0, sizeof(sin));
727 socklen_t sinlen = sizeof(sin);
728 int ret, sockerr;
729
730 // Check if listening socket is ready to accept
731 ret = IsSocketReady(uid, true, false, &sockerr);
732 if (ret > 0) {
733 // Accept Connection
734 ret = accept(uid, (struct sockaddr*)&sin, &sinlen);
735 sockerr = errno;
736 }
737
738 // Accepted New Connection
739 if (ret > 0) {
740 int newid = AcceptPtpSocket(req.id, ret, sin, req.remoteMAC, req.remotePort);
741 if (newid > 0)
742 result = newid;
743 }
744 else if (ret == 0 || (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK))) {
745 u64 now = (u64)(time_now_d() * 1000000.0);
746 if (req.timeout == 0 || now - req.startTime <= req.timeout) {
747 return -1;
748 }
749 else {
750 result = ERROR_NET_ADHOC_TIMEOUT;
751 }
752 }
753 else
754 result = ERROR_NET_ADHOC_INVALID_ARG; //ERROR_NET_ADHOC_TIMEOUT
755
756 if (ret == SOCKET_ERROR)
757 DEBUG_LOG(SCENET, "sceNetAdhocPtpAccept[%i]: Socket Error (%i)", req.id, sockerr);
758
759 return 0;
760 }
761
DoBlockingPtpConnect(int uid,AdhocSocketRequest & req,s64 & result)762 int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) {
763 auto sock = adhocSockets[req.id - 1];
764 if (!sock) {
765 result = ERROR_NET_ADHOC_SOCKET_DELETED;
766 return 0;
767 }
768 auto& ptpsocket = sock->data.ptp;
769 if (sock->flags & ADHOC_F_ALERTCONNECT) {
770 result = ERROR_NET_ADHOC_SOCKET_ALERTED;
771 sock->alerted_flags |= ADHOC_F_ALERTCONNECT;
772 return 0;
773 }
774
775 int sockerr;
776 // Wait for Connection (assuming "connect" has been called before)
777 int ret = IsSocketReady(uid, false, true, &sockerr);
778
779 // Connection is ready
780 if (ret > 0) {
781 struct sockaddr_in sin;
782 memset(&sin, 0, sizeof(sin));
783 socklen_t sinlen = sizeof(sin);
784 getpeername(uid, (struct sockaddr*)&sin, &sinlen);
785
786 // Set Connected State
787 ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED;
788
789 INFO_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Established (%s:%u)", req.id, ptpsocket.lport, ip2str(sin.sin_addr).c_str(), ptpsocket.pport);
790
791 // Success
792 result = 0;
793 }
794 // Timeout
795 else if (ret == 0) {
796 u64 now = (u64)(time_now_d() * 1000000.0);
797 if (req.timeout == 0 || now - req.startTime <= req.timeout) {
798 return -1;
799 }
800 else {
801 // Handle Workaround that force the first Connect to be blocking for issue related to lobby or high latency networks
802 if (sock->nonblocking)
803 result = ERROR_NET_ADHOC_WOULD_BLOCK;
804 else
805 result = ERROR_NET_ADHOC_TIMEOUT; // FIXME: PSP never returned ERROR_NET_ADHOC_TIMEOUT on PtpConnect? or only returned ERROR_NET_ADHOC_TIMEOUT when the host is too busy? Seems to be returning ERROR_NET_ADHOC_CONNECTION_REFUSED on timedout instead (if the other side in not listening yet).
806 }
807 }
808 // Select was interrupted or contains invalid args?
809 else
810 result = ERROR_NET_ADHOC_EXCEPTION_EVENT; // ERROR_NET_ADHOC_INVALID_ARG;
811
812 if (ret == SOCKET_ERROR)
813 DEBUG_LOG(SCENET, "sceNetAdhocPtpConnect[%i]: Socket Error (%i)", req.id, sockerr);
814
815 return 0;
816 }
817
DoBlockingPtpFlush(int uid,AdhocSocketRequest & req,s64 & result)818 int DoBlockingPtpFlush(int uid, AdhocSocketRequest& req, s64& result) {
819 auto sock = adhocSockets[req.id - 1];
820 if (!sock) {
821 result = ERROR_NET_ADHOC_SOCKET_DELETED;
822 return 0;
823 }
824 auto& ptpsocket = sock->data.ptp;
825 if (sock->flags & ADHOC_F_ALERTFLUSH) {
826 result = ERROR_NET_ADHOC_SOCKET_ALERTED;
827 sock->alerted_flags |= ADHOC_F_ALERTFLUSH;
828 return 0;
829 }
830
831 // Try Sending Empty Data
832 int sockerr = FlushPtpSocket(uid);
833 result = 0;
834
835 if (sockerr == EAGAIN || sockerr == EWOULDBLOCK) {
836 u64 now = (u64)(time_now_d() * 1000000.0);
837 if (req.timeout == 0 || now - req.startTime <= req.timeout) {
838 return -1;
839 }
840 else
841 result = ERROR_NET_ADHOC_TIMEOUT;
842 }
843 else if (isDisconnected(sockerr)) {
844 // Change Socket State. // FIXME: Does Alerted Socket should be closed too?
845 ptpsocket.state = ADHOC_PTP_STATE_CLOSED;
846
847 // Disconnected
848 result = ERROR_NET_ADHOC_DISCONNECTED;
849 }
850
851 if (sockerr != 0) {
852 DEBUG_LOG(SCENET, "sceNetAdhocPtpFlush[%i]: Socket Error (%i)", req.id, sockerr);
853 }
854
855 return 0;
856 }
857
DoBlockingAdhocPollSocket(int uid,AdhocSocketRequest & req,s64 & result)858 int DoBlockingAdhocPollSocket(int uid, AdhocSocketRequest& req, s64& result) {
859 SceNetAdhocPollSd* sds = (SceNetAdhocPollSd*)req.buffer;
860 int ret = PollAdhocSocket(sds, req.id, 0, 0);
861 if (ret <= 0) {
862 u64 now = (u64)(time_now_d() * 1000000.0);
863 // POSIX poll using negative timeout for indefinitely blocking, not sure about PSP's AdhocPollSocket tho since most of PSP's sceNet API using 0 for indefinitely blocking.
864 if (static_cast<int>(req.timeout) <= 0 || now - req.startTime <= req.timeout) {
865 return -1;
866 }
867 else if (ret < 0)
868 ret = ERROR_NET_ADHOC_EXCEPTION_EVENT;
869 // FIXME: Does AdhocPollSocket can return any error code other than ERROR_NET_ADHOC_EXCEPTION_EVENT?
870 //else
871 // ret = ERROR_NET_ADHOC_TIMEOUT;
872 }
873 result = ret;
874
875 if (ret > 0) {
876 for (int i = 0; i < req.id; i++) {
877 if (sds[i].id > 0 && sds[i].id <= MAX_SOCKET && adhocSockets[sds[i].id - 1] != NULL) {
878 auto sock = adhocSockets[sds[i].id - 1];
879 if (sock->type == SOCK_PTP)
880 VERBOSE_LOG(SCENET, "Poll PTP Socket Id: %d (%d), events: %08x, revents: %08x - state: %d", sds[i].id, sock->data.ptp.id, sds[i].events, sds[i].revents, sock->data.ptp.state);
881 else
882 VERBOSE_LOG(SCENET, "Poll PDP Socket Id: %d (%d), events: %08x, revents: %08x", sds[i].id, sock->data.pdp.id, sds[i].events, sds[i].revents);
883 }
884 }
885 }
886
887 return 0;
888 }
889
__AdhocSocketNotify(u64 userdata,int cyclesLate)890 static void __AdhocSocketNotify(u64 userdata, int cyclesLate) {
891 SceUID threadID = userdata >> 32;
892 int uid = (int)(userdata & 0xFFFFFFFF); // fd/socket id
893
894 s64 result = -1;
895 u32 error = 0;
896 int delayUS = 500;
897
898 SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error);
899 if (waitID == 0 || error != 0) {
900 WARN_LOG(SCENET, "sceNetAdhoc Socket WaitID(%i) on Thread(%i) already woken up? (error: %08x)", uid, threadID, error);
901 return;
902 }
903
904 // Socket not found?! Should never happened! But if it ever happened (ie. loaded from SaveState where adhocSocketRequests got cleared) return TIMEOUT and let the game try again.
905 if (adhocSocketRequests.find(userdata) == adhocSocketRequests.end()) {
906 WARN_LOG(SCENET, "sceNetAdhoc Socket WaitID(%i) on Thread(%i) not found!", uid, threadID);
907 __KernelResumeThreadFromWait(threadID, ERROR_NET_ADHOC_TIMEOUT);
908 return;
909 }
910
911 AdhocSocketRequest req = adhocSocketRequests[userdata];
912
913 switch (req.type) {
914 case PDP_SEND:
915 if (sendTargetPeers.find(userdata) == sendTargetPeers.end()) {
916 // No destination peers?
917 result = 0;
918 break;
919 }
920 if (DoBlockingPdpSend(uid, req, result, sendTargetPeers[userdata])) {
921 // Try again in another 0.5ms until data available or timedout.
922 CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
923 return;
924 }
925 sendTargetPeers.erase(userdata);
926 break;
927
928 case PDP_RECV:
929 if (DoBlockingPdpRecv(uid, req, result)) {
930 // Try again in another 0.5ms until data available or timedout.
931 CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
932 return;
933 }
934 break;
935
936 case PTP_SEND:
937 if (DoBlockingPtpSend(uid, req, result)) {
938 // Try again in another 0.5ms until data available or timedout.
939 CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
940 return;
941 }
942 break;
943
944 case PTP_RECV:
945 if (DoBlockingPtpRecv(uid, req, result)) {
946 // Try again in another 0.5ms until data available or timedout.
947 CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
948 return;
949 }
950 break;
951
952 case PTP_ACCEPT:
953 if (DoBlockingPtpAccept(uid, req, result)) {
954 // Try again in another 0.5ms until data available or timedout.
955 CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
956 return;
957 }
958 break;
959
960 case PTP_CONNECT:
961 if (DoBlockingPtpConnect(uid, req, result)) {
962 // Try again in another 0.5ms until data available or timedout.
963 CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
964 return;
965 }
966 break;
967
968 case PTP_FLUSH:
969 if (DoBlockingPtpFlush(uid, req, result)) {
970 // Try again in another 0.5ms until data available or timedout.
971 CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
972 return;
973 }
974 break;
975
976 case ADHOC_POLL_SOCKET:
977 if (DoBlockingAdhocPollSocket(uid, req, result)) {
978 // Try again in another 0.5ms until data available or timedout.
979 CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
980 return;
981 }
982 break;
983 }
984
985 __KernelResumeThreadFromWait(threadID, result);
986 DEBUG_LOG(SCENET, "Returning (ThreadId: %d, WaitID: %d, error: %08x) Result (%08x) of sceNetAdhoc[%d] - SocketID: %d", threadID, waitID, error, (int)result, req.type, req.id);
987
988 // We are done with this socket
989 adhocSocketRequests.erase(userdata);
990 }
991
992 // input threadSocketId = ((u64)__KernelGetCurThread()) << 32 | socketId;
WaitBlockingAdhocSocket(u64 threadSocketId,int type,int pspSocketId,void * buffer,s32_le * len,u32 timeoutUS,SceNetEtherAddr * remoteMAC,u16_le * remotePort,const char * reason)993 int WaitBlockingAdhocSocket(u64 threadSocketId, int type, int pspSocketId, void* buffer, s32_le* len, u32 timeoutUS, SceNetEtherAddr* remoteMAC, u16_le* remotePort, const char* reason) {
994 int uid = (int)(threadSocketId & 0xFFFFFFFF);
995 if (adhocSocketRequests.find(threadSocketId) != adhocSocketRequests.end()) {
996 WARN_LOG(SCENET, "sceNetAdhoc[%d] - ThreadID[%d] WaitID[%d] already existed, Socket[%d] is busy!", type, static_cast<int>(threadSocketId >> 32), uid, pspSocketId);
997 // FIXME: Not sure if Adhoc Socket can return ADHOC_BUSY or not (assuming it's similar to EINPROGRESS for Adhoc Socket), or may be we should return TIMEOUT instead?
998 return ERROR_NET_ADHOC_BUSY; // ERROR_NET_ADHOC_TIMEOUT
999 }
1000
1001 //changeBlockingMode(socketId, 1);
1002
1003 u32 tmout = timeoutUS;
1004 if (tmout > 0)
1005 tmout = std::max(tmout, minSocketTimeoutUS);
1006
1007 u64 startTime = (u64)(time_now_d() * 1000000.0);
1008 adhocSocketRequests[threadSocketId] = { type, pspSocketId, buffer, len, tmout, startTime, remoteMAC, remotePort };
1009 // Some games (ie. Hitman Reborn Battle Arena 2) are using as small as 50 usec timeout
1010 CoreTiming::ScheduleEvent(usToCycles(1), adhocSocketNotifyEvent, threadSocketId);
1011 __KernelWaitCurThread(WAITTYPE_NET, uid, 0, 0, false, reason);
1012
1013 // Fallback return value
1014 return ERROR_NET_ADHOC_TIMEOUT;
1015 }
1016
1017 // Using matchingId = -1 to delete all matching events
deleteMatchingEvents(const int matchingId=-1)1018 void deleteMatchingEvents(const int matchingId = -1) {
1019 for (auto it = matchingEvents.begin(); it != matchingEvents.end(); ) {
1020 if (matchingId < 0 || it->data[0] == matchingId) {
1021 if (Memory::IsValidAddress(it->data[2]))
1022 userMemory.Free(it->data[2]);
1023 it = matchingEvents.erase(it);
1024 }
1025 else
1026 ++it;
1027 }
1028 }
1029
netAdhocValidateLoopMemory()1030 void netAdhocValidateLoopMemory() {
1031 // Allocate Memory if it wasn't valid/allocated after loaded from old SaveState
1032 if (!dummyThreadHackAddr || (dummyThreadHackAddr && strcmp("dummythreadhack", kernelMemory.GetBlockTag(dummyThreadHackAddr)) != 0)) {
1033 u32 blockSize = sizeof(dummyThreadCode);
1034 dummyThreadHackAddr = kernelMemory.Alloc(blockSize, false, "dummythreadhack");
1035 if (dummyThreadHackAddr) Memory::Memcpy(dummyThreadHackAddr, dummyThreadCode, sizeof(dummyThreadCode));
1036 }
1037 if (!matchingThreadHackAddr || (matchingThreadHackAddr && strcmp("matchingThreadHack", kernelMemory.GetBlockTag(matchingThreadHackAddr)) != 0)) {
1038 u32 blockSize = sizeof(matchingThreadCode);
1039 matchingThreadHackAddr = kernelMemory.Alloc(blockSize, false, "matchingThreadHack");
1040 if (matchingThreadHackAddr) Memory::Memcpy(matchingThreadHackAddr, matchingThreadCode, sizeof(matchingThreadCode));
1041 }
1042 }
1043
__NetAdhocDoState(PointerWrap & p)1044 void __NetAdhocDoState(PointerWrap &p) {
1045 auto s = p.Section("sceNetAdhoc", 1, 8);
1046 if (!s)
1047 return;
1048
1049 auto cur_netAdhocInited = netAdhocInited;
1050 auto cur_netAdhocctlInited = netAdhocctlInited;
1051 auto cur_netAdhocMatchingInited = netAdhocMatchingInited;
1052
1053 Do(p, netAdhocInited);
1054 Do(p, netAdhocctlInited);
1055 Do(p, netAdhocMatchingInited);
1056 Do(p, adhocctlHandlers);
1057
1058 if (s >= 2) {
1059 Do(p, actionAfterMatchingMipsCall);
1060 if (actionAfterMatchingMipsCall != -1) {
1061 __KernelRestoreActionType(actionAfterMatchingMipsCall, AfterMatchingMipsCall::Create);
1062 }
1063
1064 Do(p, dummyThreadHackAddr);
1065 }
1066 else {
1067 actionAfterMatchingMipsCall = -1;
1068 dummyThreadHackAddr = 0;
1069 }
1070 if (s >= 3) {
1071 Do(p, actionAfterAdhocMipsCall);
1072 if (actionAfterAdhocMipsCall != -1) {
1073 __KernelRestoreActionType(actionAfterAdhocMipsCall, AfterAdhocMipsCall::Create);
1074 }
1075
1076 Do(p, matchingThreadHackAddr);
1077 }
1078 else {
1079 actionAfterAdhocMipsCall = -1;
1080 matchingThreadHackAddr = 0;
1081 }
1082 if (s >= 4) {
1083 Do(p, threadAdhocID);
1084 Do(p, matchingThreads);
1085 }
1086 else {
1087 threadAdhocID = 0;
1088 for (auto& it : matchingThreads) {
1089 it = 0;
1090 }
1091 }
1092 if (s >= 5) {
1093 Do(p, adhocConnectionType);
1094 Do(p, adhocctlState);
1095 Do(p, adhocctlNotifyEvent);
1096 Do(p, adhocSocketNotifyEvent);
1097 } else {
1098 adhocConnectionType = ADHOC_CONNECT;
1099 adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
1100 adhocctlNotifyEvent = -1;
1101 adhocSocketNotifyEvent = -1;
1102 }
1103 CoreTiming::RestoreRegisterEvent(adhocctlNotifyEvent, "__AdhocctlNotify", __AdhocctlNotify);
1104 CoreTiming::RestoreRegisterEvent(adhocSocketNotifyEvent, "__AdhocSocketNotify", __AdhocSocketNotify);
1105 if (s >= 6) {
1106 Do(p, gameModeNotifyEvent);
1107 } else {
1108 gameModeNotifyEvent = -1;
1109 }
1110 CoreTiming::RestoreRegisterEvent(gameModeNotifyEvent, "__GameModeNotify", __GameModeNotify);
1111 if (s >= 7) {
1112 Do(p, adhocctlStateEvent);
1113 } else {
1114 adhocctlStateEvent = -1;
1115 }
1116 CoreTiming::RestoreRegisterEvent(adhocctlStateEvent, "__AdhocctlState", __AdhocctlState);
1117 if (s >= 8) {
1118 Do(p, isAdhocctlBusy);
1119 Do(p, netAdhocGameModeEntered);
1120 Do(p, netAdhocEnterGameModeTimeout);
1121 }
1122 else {
1123 isAdhocctlBusy = false;
1124 netAdhocGameModeEntered = false;
1125 netAdhocEnterGameModeTimeout = 15000000;
1126 }
1127
1128 if (p.mode == p.MODE_READ) {
1129 // Discard leftover events
1130 adhocctlEvents.clear();
1131 adhocctlRequests.clear();
1132 adhocSocketRequests.clear();
1133 sendTargetPeers.clear();
1134 deleteAllAdhocSockets();
1135 deleteMatchingEvents();
1136
1137 // Let's not change "Inited" value when Loading SaveState to prevent memory & port leaks
1138 netAdhocMatchingInited = cur_netAdhocMatchingInited;
1139 netAdhocctlInited = cur_netAdhocctlInited;
1140 netAdhocInited = cur_netAdhocInited;
1141
1142 isAdhocctlNeedLogin = false;
1143 }
1144 }
1145
__UpdateAdhocctlHandlers(u32 flag,u32 error)1146 void __UpdateAdhocctlHandlers(u32 flag, u32 error) {
1147 std::lock_guard<std::recursive_mutex> adhocGuard(adhocEvtMtx);
1148 adhocctlEvents.push_back({ flag, error });
1149 }
1150
__UpdateMatchingHandler(MatchingArgs ArgsPtr)1151 void __UpdateMatchingHandler(MatchingArgs ArgsPtr) {
1152 std::lock_guard<std::recursive_mutex> adhocGuard(adhocEvtMtx);
1153 matchingEvents.push_back(ArgsPtr);
1154 }
1155
__CreateHLELoop(u32_le * loopAddr,const char * sceFuncName,const char * hleFuncName,const char * tagName)1156 u32_le __CreateHLELoop(u32_le *loopAddr, const char *sceFuncName, const char *hleFuncName, const char *tagName) {
1157 if (loopAddr == NULL || sceFuncName == NULL || hleFuncName == NULL)
1158 return 0;
1159
1160 loopAddr[0] = MIPS_MAKE_SYSCALL(sceFuncName, hleFuncName);
1161 loopAddr[1] = MIPS_MAKE_B(-2);
1162 loopAddr[2] = MIPS_MAKE_NOP();
1163 u32 blockSize = sizeof(u32_le)*3;
1164 u32_le dummyThreadHackAddr = kernelMemory.Alloc(blockSize, false, tagName); // blockSize will be rounded to 256 granularity
1165 Memory::Memcpy(dummyThreadHackAddr, loopAddr, sizeof(u32_le) * 3); // This area will be cleared again after loading an old savestate :(
1166 return dummyThreadHackAddr;
1167 }
1168
__AdhocNotifInit()1169 void __AdhocNotifInit() {
1170 adhocctlNotifyEvent = CoreTiming::RegisterEvent("__AdhocctlNotify", __AdhocctlNotify);
1171 adhocSocketNotifyEvent = CoreTiming::RegisterEvent("__AdhocSocketNotify", __AdhocSocketNotify);
1172 gameModeNotifyEvent = CoreTiming::RegisterEvent("__GameModeNotify", __GameModeNotify);
1173 adhocctlStateEvent = CoreTiming::RegisterEvent("__AdhocctlState", __AdhocctlState);
1174
1175 adhocctlRequests.clear();
1176 adhocSocketRequests.clear();
1177 sendTargetPeers.clear();
1178 }
1179
__NetAdhocInit()1180 void __NetAdhocInit() {
1181 friendFinderRunning = false;
1182 netAdhocInited = false;
1183 netAdhocctlInited = false;
1184 netAdhocMatchingInited = false;
1185 adhocctlHandlers.clear();
1186 __AdhocNotifInit();
1187 __AdhocServerInit();
1188
1189 // Create built-in AdhocServer Thread
1190 adhocServerRunning = false;
1191 if (g_Config.bEnableWlan && g_Config.bEnableAdhocServer) {
1192 adhocServerThread = std::thread(proAdhocServerThread, SERVER_PORT);
1193 }
1194 }
1195
sceNetAdhocInit()1196 u32 sceNetAdhocInit() {
1197 if (!netAdhocInited) {
1198 // Library initialized
1199 netAdhocInited = true;
1200 isAdhocctlBusy = false;
1201
1202 // FIXME: It seems official prx is using sceNetAdhocGameModeDeleteMaster in here?
1203 NetAdhocGameMode_DeleteMaster();
1204 // Since we are deleting GameMode Master here, we should probably need to make sure GameMode resources all cleared too.
1205 deleteAllGMB();
1206
1207 // Return Success
1208 return hleLogSuccessInfoI(SCENET, 0, "at %08x", currentMIPS->pc);
1209 }
1210 // Already initialized
1211 return hleLogWarning(SCENET, ERROR_NET_ADHOC_ALREADY_INITIALIZED, "already initialized");
1212 }
1213
sceNetAdhocctlInit(int stackSize,int prio,u32 productAddr)1214 static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) {
1215 INFO_LOG(SCENET, "sceNetAdhocctlInit(%i, %i, %08x) at %08x", stackSize, prio, productAddr, currentMIPS->pc);
1216
1217 // FIXME: Returning 0x8002013a (SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED) without adhoc module loaded first?
1218 // FIXME: Sometimes returning 0x80410601 (ERROR_NET_ADHOC_AUTH_ALREADY_INITIALIZED / Library module is already initialized ?) when AdhocctlTerm is not fully done?
1219
1220 if (netAdhocctlInited)
1221 return ERROR_NET_ADHOCCTL_ALREADY_INITIALIZED;
1222
1223 if (Memory::IsValidAddress(productAddr)) {
1224 Memory::ReadStruct(productAddr, &product_code);
1225 }
1226
1227 adhocctlEvents.clear();
1228 netAdhocctlInited = true; //needed for cleanup during AdhocctlTerm even when it failed to connect to Adhoc Server (since it's being faked as success)
1229 isAdhocctlNeedLogin = true;
1230
1231 // Create fake PSP Thread for callback
1232 // TODO: Should use a separated threads for friendFinder, matchingEvent, and matchingInput and created on AdhocctlInit & AdhocMatchingStart instead of here
1233 netAdhocValidateLoopMemory();
1234 threadAdhocID = __KernelCreateThread("AdhocThread", __KernelGetCurThreadModuleId(), dummyThreadHackAddr, prio, stackSize, PSP_THREAD_ATTR_USER, 0, true);
1235 if (threadAdhocID > 0) {
1236 __KernelStartThread(threadAdhocID, 0, 0);
1237 }
1238
1239 // TODO: Merging friendFinder (real) thread to AdhocThread (fake) thread on PSP side
1240 if (!friendFinderRunning) {
1241 friendFinderThread = std::thread(friendFinder);
1242 }
1243
1244 // Need to make sure to be connected to Adhoc Server (indicated by networkInited) before returning to prevent GTA VCS failed to create/join a group and unable to see any game room
1245 int us = adhocDefaultDelay;
1246 if (g_Config.bEnableWlan && !networkInited) {
1247 AdhocctlRequest dummyreq = { OPCODE_LOGIN, {0} };
1248 return WaitBlockingAdhocctlSocket(dummyreq, us, "adhocctl init");
1249 }
1250 // Give a little time for friendFinder thread to be ready before the game use the next sceNet functions, should've checked for friendFinderRunning status instead of guessing the time?
1251 hleEatMicro(us);
1252
1253 return 0;
1254 }
1255
NetAdhocctl_GetState()1256 int NetAdhocctl_GetState() {
1257 return adhocctlState;
1258 }
1259
sceNetAdhocctlGetState(u32 ptrToStatus)1260 int sceNetAdhocctlGetState(u32 ptrToStatus) {
1261 // Library uninitialized
1262 if (!netAdhocctlInited)
1263 return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
1264
1265 // Invalid Arguments
1266 if (!Memory::IsValidAddress(ptrToStatus))
1267 return ERROR_NET_ADHOCCTL_INVALID_ARG;
1268
1269 int state = NetAdhocctl_GetState();
1270 // Output Adhocctl State
1271 Memory::Write_U32(state, ptrToStatus);
1272
1273 // Return Success
1274 return hleLogSuccessVerboseI(SCENET, 0, "state = %d", state);
1275 }
1276
1277 /**
1278 * Adhoc Emulator PDP Socket Creator
1279 * @param saddr Local MAC (Unused)
1280 * @param sport Local Binding Port
1281 * @param bufsize Socket Buffer Size
1282 * @param flag Bitflags (Unused)
1283 * @return Socket ID > 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_SOCKET_ID_NOT_AVAIL, ADHOC_INVALID_ADDR, ADHOC_PORT_NOT_AVAIL, ADHOC_INVALID_PORT, ADHOC_PORT_IN_USE, NET_NO_SPACE
1284 */
1285 // When choosing AdHoc menu in Wipeout Pulse sometimes it's saying that "WLAN is turned off" on game screen and getting "kUnityCommandCode_MediaDisconnected" error in the Log Console when calling sceNetAdhocPdpCreate, probably it needed to wait something from the thread before calling this (ie. need to receives 7 bytes from adhoc server 1st?)
sceNetAdhocPdpCreate(const char * mac,int port,int bufferSize,u32 flag)1286 static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 flag) {
1287 INFO_LOG(SCENET, "sceNetAdhocPdpCreate(%s, %u, %u, %u) at %08x", mac2str((SceNetEtherAddr*)mac).c_str(), port, bufferSize, flag, currentMIPS->pc);
1288 if (!g_Config.bEnableWlan) {
1289 return -1;
1290 }
1291
1292 // Library is initialized
1293 SceNetEtherAddr * saddr = (SceNetEtherAddr *)mac;
1294 if (netAdhocInited) {
1295 // Valid Arguments are supplied
1296 if (mac != NULL && bufferSize > 0) {
1297 // Port is in use by another PDP Socket.
1298 if (isPDPPortInUse(port)) {
1299 // FIXME: When PORT_IN_USE error occured it seems the index to the socket id also increased, which means it tries to create & bind the socket first and then closes it due to failed to bind
1300 return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_IN_USE, "port in use");
1301 }
1302
1303 //sport 0 should be shifted back to 0 when using offset Phantasy Star Portable 2 use this
1304 if (port == 0) port = -static_cast<int>(portOffset);
1305 // Some games (ie. DBZ Shin Budokai 2) might be getting the saddr/srcmac content from SaveState and causing problems :( So we try to fix it here
1306 if (saddr != NULL) {
1307 getLocalMac(saddr);
1308 }
1309 // Valid MAC supplied. FIXME: MAC only valid after successful attempt to Create/Connect/Join a Group? (ie. adhocctlCurrentMode != ADHOCCTL_MODE_NONE)
1310 if ((adhocctlCurrentMode != ADHOCCTL_MODE_NONE) && isLocalMAC(saddr)) {
1311 // Create Internet UDP Socket
1312 int usocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1313 // Valid Socket produced
1314 if (usocket != INVALID_SOCKET) {
1315 // Change socket buffer size to be consistent on all platforms.
1316 // Send Buffer should be smaller than Recv Buffer to prevent faster device from flooding slower device too much.
1317 setSockBufferSize(usocket, SO_SNDBUF, bufferSize*5); //PSP_ADHOC_PDP_MFS
1318 // Recv Buffer should be equal or larger than Send Buffer. Using larger Recv Buffer might helped reduces dropped packets during a slowdown, but too large may cause slow performance on Warriors Orochi 2.
1319 setSockBufferSize(usocket, SO_RCVBUF, bufferSize*10); //PSP_ADHOC_PDP_MFS*10
1320
1321 // Ignore SIGPIPE when supported (ie. BSD/MacOS)
1322 setSockNoSIGPIPE(usocket, 1);
1323
1324 // Enable Port Re-use, this will allow binding to an already used port, but only one of them can read the data (shared receive buffer?)
1325 setSockReuseAddrPort(usocket);
1326
1327 // Disable Connection Reset error on UDP to avoid strange behavior https://stackoverflow.com/questions/34242622/windows-udp-sockets-recvfrom-fails-with-error-10054
1328 setUDPConnReset(usocket, false);
1329
1330 // Binding Information for local Port
1331 struct sockaddr_in addr;
1332 addr.sin_family = AF_INET;
1333 addr.sin_addr.s_addr = INADDR_ANY;
1334 if (isLocalServer) {
1335 getLocalIp(&addr);
1336 }
1337 uint16_t requestedport = static_cast<int>(port + static_cast<int>(portOffset));
1338 // Avoid getting random port due to port offset when original port wasn't 0 (ie. original_port + port_offset = 65536 = 0)
1339 if (requestedport == 0 && port > 0)
1340 requestedport = 65535; // Hopefully it will be safe to default it to 65535 since there can't be more than one port that can bumped into 65536
1341 // Show a warning about privileged ports
1342 if (requestedport != 0 && requestedport < 1024) {
1343 WARN_LOG(SCENET, "sceNetAdhocPdpCreate - Ports below 1024(ie. %hu) may require Admin Privileges", requestedport);
1344 }
1345 addr.sin_port = htons(requestedport);
1346
1347 // Bound Socket to local Port
1348 int iResult = bind(usocket, (struct sockaddr*)&addr, sizeof(addr));
1349
1350 if (iResult == 0) {
1351 // Workaround: Send a dummy 0 size message to AdhocServer IP to make sure the socket actually bound to an address when binded with INADDR_ANY before using getsockname, seems to fix sending from incorrect port issue on MGS:PW on Android
1352 addr.sin_addr.s_addr = g_adhocServerIP.in.sin_addr.s_addr;
1353 addr.sin_port = 0;
1354 sendto(usocket, dummyPeekBuf64k, 0, MSG_NOSIGNAL, (struct sockaddr*)&addr, sizeof(addr));
1355 // Update sport with the port assigned internal->lport = ntohs(local.sin_port)
1356 socklen_t len = sizeof(addr);
1357 if (getsockname(usocket, (struct sockaddr*)&addr, &len) == 0) {
1358 uint16_t boundport = ntohs(addr.sin_port);
1359 if (port + static_cast<int>(portOffset) >= 65536 || static_cast<int>(boundport) - static_cast<int>(portOffset) <= 0)
1360 WARN_LOG(SCENET, "sceNetAdhocPdpCreate - Wrapped Port Detected: Original(%d) -> Requested(%d), Bound(%d) -> BoundOriginal(%d)", port, requestedport, boundport, boundport - portOffset);
1361 port = boundport - portOffset;
1362 }
1363
1364 // Allocate Memory for Internal Data
1365 AdhocSocket * internal = (AdhocSocket*)malloc(sizeof(AdhocSocket));
1366
1367 // Allocated Memory
1368 if (internal != NULL) {
1369 // Find Free Translator Index
1370 // FIXME: We should probably use an increasing index instead of looking for an empty slot from beginning if we want to simulate a real socket id
1371 int i = 0;
1372 for (; i < MAX_SOCKET; i++) if (adhocSockets[i] == NULL) break;
1373
1374 // Found Free Translator Index
1375 if (i < MAX_SOCKET) {
1376 // Clear Memory
1377 memset(internal, 0, sizeof(AdhocSocket));
1378
1379 // Socket Type
1380 internal->type = SOCK_PDP;
1381 internal->nonblocking = flag;
1382 internal->buffer_size = bufferSize;
1383
1384 // Fill in Data
1385 internal->data.pdp.id = usocket;
1386 internal->data.pdp.laddr = *saddr;
1387 internal->data.pdp.lport = port; //getLocalPort(usocket) - portOffset;
1388
1389 // Link Socket to Translator ID
1390 adhocSockets[i] = internal;
1391
1392 // Forward Port on Router
1393 //sceNetPortOpen("UDP", port);
1394 UPnP_Add(IP_PROTOCOL_UDP, isOriPort ? port : port + portOffset, port + portOffset); // g_PortManager.Add(IP_PROTOCOL_UDP, isOriPort ? port : port + portOffset, port + portOffset);
1395
1396 // Switch to non-blocking for futher usage
1397 changeBlockingMode(usocket, 1);
1398
1399 // Success
1400 return hleLogDebug(SCENET, i + 1, "success");
1401 }
1402
1403 // Free Memory for Internal Data
1404 free(internal);
1405 }
1406 }
1407
1408 // Close Socket
1409 closesocket(usocket);
1410
1411 // Port not available (exclusively in use?)
1412 if (iResult == SOCKET_ERROR) {
1413 ERROR_LOG(SCENET, "Socket error (%i) when binding port %u", errno, ntohs(addr.sin_port));
1414 auto n = GetI18NCategory("Networking");
1415 host->NotifyUserMessage(std::string(n->T("Failed to Bind Port")) + " " + std::to_string(port + portOffset) + "\n" + std::string(n->T("Please change your Port Offset")), 3.0, 0x0000ff);
1416
1417 return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_NOT_AVAIL, "port not available");
1418 }
1419 }
1420
1421 // Default to No-Space Error
1422 return hleLogDebug(SCENET, ERROR_NET_NO_SPACE, "net no space");
1423 }
1424
1425 // Invalid MAC supplied
1426 return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ADDR, "invalid address");
1427 }
1428
1429 // Invalid Arguments were supplied
1430 return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg");
1431 }
1432 // Library is uninitialized
1433 return hleLogDebug(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "adhoc not initialized");
1434 }
1435
1436 /**
1437 * Get Adhoc Parameter
1438 * @param parameter OUT: Adhoc Parameter
1439 * @return 0 on success or... ADHOCCTL_NOT_INITIALIZED, ADHOCCTL_INVALID_ARG
1440 */
sceNetAdhocctlGetParameter(u32 paramAddr)1441 static int sceNetAdhocctlGetParameter(u32 paramAddr) {
1442 char grpName[9] = { 0 };
1443 memcpy(grpName, parameter.group_name.data, ADHOCCTL_GROUPNAME_LEN);
1444 parameter.nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;
1445 DEBUG_LOG(SCENET, "sceNetAdhocctlGetParameter(%08x) [Ch=%i][Group=%s][BSSID=%s][name=%s]", paramAddr, parameter.channel, grpName, mac2str(¶meter.bssid.mac_addr).c_str(), parameter.nickname.data);
1446 if (!g_Config.bEnableWlan) {
1447 return ERROR_NET_ADHOCCTL_DISCONNECTED;
1448 }
1449
1450 // Library initialized
1451 if (netAdhocctlInited) {
1452 // Valid Arguments
1453 if (Memory::IsValidAddress(paramAddr)) {
1454 // Copy Parameter
1455 Memory::WriteStruct(paramAddr,¶meter);
1456 // Return Success
1457 return 0;
1458 }
1459
1460 // Invalid Arguments
1461 return ERROR_NET_ADHOCCTL_INVALID_ARG;
1462 }
1463
1464 // Library uninitialized
1465 return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
1466 }
1467
1468 /**
1469 * Adhoc Emulator PDP Send Call
1470 * @param id Socket File Descriptor
1471 * @param daddr Target MAC Address
1472 * @param dport Target Port
1473 * @param data Data Payload
1474 * @param len Payload Length
1475 * @param timeout Send Timeout (microseconds)
1476 * @param flag Nonblocking Flag
1477 * @return 0 on success or... ADHOC_INVALID_ARG, ADHOC_NOT_INITIALIZED, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_INVALID_ADDR, ADHOC_INVALID_PORT, ADHOC_INVALID_DATALEN, ADHOC_SOCKET_ALERTED, ADHOC_TIMEOUT, ADHOC_THREAD_ABORTED, ADHOC_WOULD_BLOCK, NET_NO_SPACE, NET_INTERNAL
1478 */
sceNetAdhocPdpSend(int id,const char * mac,u32 port,void * data,int len,int timeout,int flag)1479 static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int len, int timeout, int flag) {
1480 if (flag == 0) { // Prevent spamming Debug Log with retries of non-bocking socket
1481 DEBUG_LOG(SCENET, "sceNetAdhocPdpSend(%i, %s, %i, %p, %i, %i, %i) at %08x", id, mac2str((SceNetEtherAddr*)mac).c_str(), port, data, len, timeout, flag, currentMIPS->pc);
1482 } else {
1483 VERBOSE_LOG(SCENET, "sceNetAdhocPdpSend(%i, %s, %i, %p, %i, %i, %i) at %08x", id, mac2str((SceNetEtherAddr*)mac).c_str(), port, data, len, timeout, flag, currentMIPS->pc);
1484 }
1485 if (!g_Config.bEnableWlan) {
1486 return -1;
1487 }
1488 SceNetEtherAddr * daddr = (SceNetEtherAddr *)mac;
1489 uint16_t dport = (uint16_t)port;
1490
1491 //if (dport < 7) dport += 1341;
1492
1493 // Really should flatten this with early outs, all this indentation is making me dizzy.
1494
1495 // Library is initialized
1496 if (netAdhocInited) {
1497 // Valid Port
1498 if (dport != 0) {
1499 // Valid Data Length
1500 if (len >= 0) { // should we allow 0 size packet (for ping) ?
1501 // Valid Socket ID
1502 if (id > 0 && id <= MAX_SOCKET && adhocSockets[id - 1] != NULL) {
1503 // Cast Socket
1504 auto socket = adhocSockets[id - 1];
1505 auto& pdpsocket = socket->data.pdp;
1506 socket->nonblocking = flag;
1507
1508 // Valid Data Buffer
1509 if (data != NULL) {
1510 // Valid Destination Address
1511 if (daddr != NULL) {
1512 // Log Destination
1513 // Schedule Timeout Removal
1514 //if (flag) timeout = 0;
1515
1516 // Apply Send Timeout Settings to Socket
1517 if (timeout > 0)
1518 setSockTimeout(pdpsocket.id, SO_SNDTIMEO, timeout);
1519
1520 if (socket->flags & ADHOC_F_ALERTSEND) {
1521 socket->alerted_flags |= ADHOC_F_ALERTSEND;
1522
1523 return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted");
1524 }
1525
1526 // Single Target
1527 if (!isBroadcastMAC(daddr)) {
1528 // Fill in Target Structure
1529 struct sockaddr_in target;
1530 target.sin_family = AF_INET;
1531 target.sin_port = htons(dport + portOffset);
1532
1533 // Get Peer IP. Some games (ie. Vulcanus Seek and Destroy) seems to try to send to zero-MAC (ie. 00:00:00:00:00:00) first before sending to the actual destination MAC.. So may be sending to zero-MAC has a special meaning? (ie. to peek send buffer availability may be?)
1534 if (resolveMAC((SceNetEtherAddr *)daddr, (uint32_t *)&target.sin_addr.s_addr)) {
1535 // Some games (ie. PSP2) might try to talk to it's self, not sure if they talked through WAN or LAN when using public Adhoc Server tho
1536 target.sin_port = htons(dport + ((isOriPort && !isPrivateIP(target.sin_addr.s_addr)) ? 0 : portOffset));
1537
1538 // Acquire Network Lock
1539 //_acquireNetworkLock();
1540
1541 // Send Data. UDP are guaranteed to be sent as a whole or nothing(failed if len > SO_MAX_MSG_SIZE), and never be partially sent/recv
1542 int sent = sendto(pdpsocket.id, (const char *)data, len, MSG_NOSIGNAL, (struct sockaddr*)&target, sizeof(target));
1543 int error = errno;
1544
1545 if (sent == SOCKET_ERROR) {
1546 // Simulate blocking behaviour with non-blocking socket
1547 if (!flag && (error == EAGAIN || error == EWOULDBLOCK)) {
1548 u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | pdpsocket.id;
1549 if (sendTargetPeers.find(threadSocketId) != sendTargetPeers.end()) {
1550 DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Socket(%d) is Busy!", id, getLocalPort(pdpsocket.id), pdpsocket.id);
1551 return hleLogError(SCENET, ERROR_NET_ADHOC_BUSY, "busy?");
1552 }
1553
1554 AdhocSendTargets dest = { len, {}, false };
1555 dest.peers.push_back({ target.sin_addr.s_addr, dport });
1556 sendTargetPeers[threadSocketId] = dest;
1557 return WaitBlockingAdhocSocket(threadSocketId, PDP_SEND, id, data, nullptr, timeout, nullptr, nullptr, "pdp send");
1558 }
1559
1560 DEBUG_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend[%i:%u->%u] (size=%i)", error, id, getLocalPort(pdpsocket.id), ntohs(target.sin_port), len);
1561 }
1562 //changeBlockingMode(socket->id, 0);
1563
1564 // Free Network Lock
1565 //_freeNetworkLock();
1566
1567 hleEatMicro(50); // Can be longer than 1ms tho
1568 // Sent Data
1569 if (sent >= 0) {
1570 DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Sent %u bytes to %s:%u\n", id, getLocalPort(pdpsocket.id), sent, ip2str(target.sin_addr).c_str(), ntohs(target.sin_port));
1571
1572 // Success
1573 return 0; // sent; // MotorStorm will try to resend if return value is not 0
1574 }
1575
1576 // Non-Blocking
1577 if (flag)
1578 return hleLogSuccessVerboseX(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block");
1579
1580 // Does PDP can Timeout? There is no concept of Timeout when sending UDP due to no ACK, but might happen if the socket buffer is full, not sure about PDP since some games did use the timeout arg
1581 return hleLogDebug(SCENET, ERROR_NET_ADHOC_TIMEOUT, "timeout?"); // ERROR_NET_ADHOC_INVALID_ADDR;
1582 }
1583 //VERBOSE_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Unknown Target Peer %s:%u\n", id, getLocalPort(pdpsocket.id), mac2str(daddr, tmpmac), ntohs(target.sin_port));
1584 }
1585
1586 // Broadcast Target
1587 else {
1588 // Acquire Network Lock
1589 //_acquireNetworkLock();
1590
1591 #ifdef BROADCAST_TO_LOCALHOST
1592 //// Get Local IP Address
1593 //union SceNetApctlInfo info; if (sceNetApctlGetInfo(PSP_NET_APCTL_INFO_IP, &info) == 0) {
1594 // // Fill in Target Structure
1595 // SceNetInetSockaddrIn target;
1596 // target.sin_family = AF_INET;
1597 // sceNetInetInetAton(info.ip, &target.sin_addr);
1598 // target.sin_port = sceNetHtons(dport);
1599 //
1600 // // Send Data
1601 // sceNetInetSendto(socket->id, data, len, ((flag != 0) ? (INET_MSG_DONTWAIT) : (0)), (SceNetInetSockaddr *)&target, sizeof(target));
1602 //}
1603 #endif
1604
1605 // Acquire Peer Lock
1606 peerlock.lock();
1607 AdhocSendTargets dest = { len, {}, true };
1608 // Iterate Peers
1609 SceNetAdhocctlPeerInfo* peer = friends;
1610 for (; peer != NULL; peer = peer->next) {
1611 // Does Skipping sending to timed out friends could cause desync when players moving group at the time MP game started?
1612 if (peer->last_recv == 0)
1613 continue;
1614
1615 dest.peers.push_back({ peer->ip_addr, dport });
1616 }
1617 // Free Peer Lock
1618 peerlock.unlock();
1619
1620 // Send Data
1621 // Simulate blocking behaviour with non-blocking socket
1622 if (!flag) {
1623 u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | pdpsocket.id;
1624 if (sendTargetPeers.find(threadSocketId) != sendTargetPeers.end()) {
1625 DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](BC): Socket(%d) is Busy!", id, getLocalPort(pdpsocket.id), pdpsocket.id);
1626 return hleLogError(SCENET, ERROR_NET_ADHOC_BUSY, "busy?");
1627 }
1628
1629 sendTargetPeers[threadSocketId] = dest;
1630 return WaitBlockingAdhocSocket(threadSocketId, PDP_SEND, id, data, nullptr, timeout, nullptr, nullptr, "pdp send broadcast");
1631 }
1632 // Non-blocking
1633 else {
1634 // Iterate Peers
1635 for (auto peer : dest.peers) {
1636 // Fill in Target Structure
1637 struct sockaddr_in target;
1638 target.sin_family = AF_INET;
1639 target.sin_addr.s_addr = peer.ip;
1640 target.sin_port = htons(dport + ((isOriPort && !isPrivateIP(peer.ip)) ? 0 : portOffset));
1641
1642 int sent = sendto(pdpsocket.id, (const char*)data, len, MSG_NOSIGNAL, (struct sockaddr*)&target, sizeof(target));
1643 int error = errno;
1644 if (sent == SOCKET_ERROR) {
1645 DEBUG_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend[%i:%u->%u](BC) [size=%i]", error, id, getLocalPort(pdpsocket.id), ntohs(target.sin_port), len);
1646 }
1647
1648 if (sent >= 0) {
1649 DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](BC): Sent %u bytes to %s:%u\n", id, getLocalPort(pdpsocket.id), sent, ip2str(target.sin_addr).c_str(), ntohs(target.sin_port));
1650 }
1651 }
1652 }
1653
1654 //changeBlockingMode(socket->id, 0);
1655
1656 // Free Network Lock
1657 //_freeNetworkLock();
1658
1659 hleEatMicro(50);
1660 // Success, Broadcast never fails!
1661 return 0; // len;
1662 }
1663 }
1664
1665 // Invalid Destination Address
1666 return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_ADDR, "invalid address");
1667 }
1668
1669 // Invalid Argument
1670 return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg");
1671 }
1672
1673 // Invalid Socket ID
1674 return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_SOCKET_ID, "invalid socket id");
1675 }
1676
1677 // Invalid Data Length
1678 return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_DATALEN, "invalid data length");
1679 }
1680
1681 // Invalid Destination Port
1682 return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_PORT, "invalid port");
1683 }
1684
1685 // Library is uninitialized
1686 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "not initialized");
1687 }
1688
1689 /**
1690 * Adhoc Emulator PDP Receive Call
1691 * @param id Socket File Descriptor
1692 * @param saddr OUT: Source MAC Address
1693 * @param sport OUT: Source Port
1694 * @param buf OUT: Received Data. The caller has to provide enough space (the whole socket buffer size?) to fully read the available packet.
1695 * @param len IN: Buffer Size OUT: Received Data Length
1696 * @param timeout Receive Timeout
1697 * @param flag Nonblocking Flag
1698 * @return 0 (or Number of bytes received?) on success or... ADHOC_INVALID_ARG, ADHOC_NOT_INITIALIZED, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_NOT_ENOUGH_SPACE, ADHOC_THREAD_ABORTED, NET_INTERNAL
1699 */
sceNetAdhocPdpRecv(int id,void * addr,void * port,void * buf,void * dataLength,u32 timeout,int flag)1700 static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void *dataLength, u32 timeout, int flag) {
1701 if (flag == 0) { // Prevent spamming Debug Log with retries of non-bocking socket
1702 DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv(%i, %p, %p, %p, %p, %i, %i) at %08x", id, addr, port, buf, dataLength, timeout, flag, currentMIPS->pc);
1703 } else {
1704 VERBOSE_LOG(SCENET, "sceNetAdhocPdpRecv(%i, %p, %p, %p, %p, %i, %i) at %08x", id, addr, port, buf, dataLength, timeout, flag, currentMIPS->pc);
1705 }
1706
1707 if (!g_Config.bEnableWlan) {
1708 return -1;
1709 }
1710
1711 SceNetEtherAddr *saddr = (SceNetEtherAddr *)addr;
1712 uint16_t * sport = (uint16_t *)port; //Looking at Quake3 sourcecode (net_adhoc.c) this is an "int" (32bit) but changing here to 32bit will cause FF-Type0 to see duplicated Host (thinking it was from a different host)
1713 int * len = (int *)dataLength;
1714 if (netAdhocInited) {
1715 // Valid Socket ID
1716 if (id > 0 && id <= MAX_SOCKET && adhocSockets[id - 1] != NULL) {
1717 // Cast Socket
1718 auto socket = adhocSockets[id - 1];
1719 auto& pdpsocket = socket->data.pdp;
1720 socket->nonblocking = flag;
1721
1722 // Valid Arguments
1723 if (saddr != NULL && port != NULL && buf != NULL && len != NULL) {
1724 #ifndef PDP_DIRTY_MAGIC
1725 // Schedule Timeout Removal
1726 //if (flag == 1) timeout = 0;
1727 #else
1728 // Nonblocking Simulator
1729 int wouldblock = 0;
1730
1731 // Minimum Timeout
1732 uint32_t mintimeout = minSocketTimeoutUS; // 250000;
1733
1734 // Nonblocking Call
1735 if (flag == 1) {
1736 // Erase Nonblocking Flag
1737 flag = 0;
1738
1739 // Set Wouldblock Behaviour
1740 wouldblock = 1;
1741
1742 // Set Minimum Timeout (250ms)
1743 if (timeout < mintimeout) timeout = mintimeout;
1744 }
1745 #endif
1746
1747 // Apply Receive Timeout Settings to Socket. Let's not wait forever (0 = indefinitely)
1748 if (timeout > 0)
1749 setSockTimeout(pdpsocket.id, SO_RCVTIMEO, timeout);
1750
1751 if (socket->flags & ADHOC_F_ALERTRECV) {
1752 socket->alerted_flags |= ADHOC_F_ALERTRECV;
1753
1754 return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted");
1755 }
1756
1757 // Sender Address
1758 struct sockaddr_in sin;
1759
1760 // Set Address Length (so we get the sender ip)
1761 socklen_t sinlen = sizeof(sin);
1762 memset(&sin, 0, sinlen);
1763 //sin.sin_len = (uint8_t)sinlen;
1764
1765 // Acquire Network Lock
1766 //_acquireNetworkLock();
1767
1768 // TODO: Use a different thread (similar to sceIo) for recvfrom, recv & accept to prevent blocking-socket from blocking emulation (ie. Hot Shots Tennis:GaG)
1769 int received = 0;
1770 int error = 0;
1771
1772 // Receive Data. PDP always sent in full size or nothing(failed), recvfrom will always receive in full size as requested (blocking) or failed (non-blocking). If available UDP data is larger than buffer, excess data is lost.
1773 // Should peek first for the available data size if it's more than len return ERROR_NET_ADHOC_NOT_ENOUGH_SPACE along with required size in len to prevent losing excess data
1774 // On Windows: MSG_TRUNC are not supported on recvfrom (socket error WSAEOPNOTSUPP), so we use dummy buffer as an alternative
1775 received = recvfrom(pdpsocket.id, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
1776 if (received > 0 && *len > 0)
1777 memcpy(buf, dummyPeekBuf64k, std::min(received, *len));
1778
1779 if (received != SOCKET_ERROR && *len < received) {
1780 INFO_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Peeked %u/%u bytes from %s:%u\n", id, getLocalPort(pdpsocket.id), received, *len, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
1781 *len = received;
1782
1783 // Peer MAC
1784 SceNetEtherAddr mac;
1785
1786 // Find Peer MAC
1787 if (resolveIP(sin.sin_addr.s_addr, &mac)) {
1788 // Provide Sender Information
1789 *saddr = mac;
1790 *sport = ntohs(sin.sin_port) - portOffset;
1791
1792 // Update last recv timestamp, may cause disconnection not detected properly tho
1793 peerlock.lock();
1794 auto peer = findFriend(&mac);
1795 if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
1796 peerlock.unlock();
1797 }
1798
1799 // Free Network Lock
1800 //_freeNetworkLock();
1801
1802 return hleLogSuccessVerboseX(SCENET, ERROR_NET_ADHOC_NOT_ENOUGH_SPACE, "not enough space"); //received;
1803 }
1804 sinlen = sizeof(sin);
1805 memset(&sin, 0, sinlen);
1806 // On Windows: Socket Error 10014 may happen when buffer size is less than the minimum allowed/required (ie. negative number on Vulcanus Seek and Destroy), the address is not a valid part of the user address space (ie. on the stack or when buffer overflow occurred), or the address is not properly aligned (ie. multiple of 4 on 32bit and multiple of 8 on 64bit) https://stackoverflow.com/questions/861154/winsock-error-code-10014
1807 received = recvfrom(pdpsocket.id, (char*)buf, std::max(0, *len), MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
1808 error = errno;
1809
1810 // On Windows: recvfrom on UDP can get error WSAECONNRESET when previous sendto's destination is unreachable (or destination port is not bound), may need to disable SIO_UDP_CONNRESET
1811 if (received == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK || error == ECONNRESET)) {
1812 if (flag == 0) {
1813 // Simulate blocking behaviour with non-blocking socket
1814 u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | pdpsocket.id;
1815 return WaitBlockingAdhocSocket(threadSocketId, PDP_RECV, id, buf, len, timeout, saddr, sport, "pdp recv");
1816 }
1817
1818 VERBOSE_LOG(SCENET, "%08x=sceNetAdhocPdpRecv: would block", ERROR_NET_ADHOC_WOULD_BLOCK); // Temporary fix to avoid a crash on the Logs due to trying to Logs syscall's argument from another thread (ie. AdhocMatchingInput thread)
1819 return ERROR_NET_ADHOC_WOULD_BLOCK; // hleLogSuccessVerboseX(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block");
1820 }
1821
1822 hleEatMicro(50);
1823 // Received Data. UDP can also receives 0 data, while on TCP 0 data = connection gracefully closed, but not sure about PDP tho
1824 if (received >= 0) {
1825 DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", id, getLocalPort(pdpsocket.id), received, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
1826
1827 // Peer MAC
1828 SceNetEtherAddr mac;
1829
1830 // Find Peer MAC
1831 if (resolveIP(sin.sin_addr.s_addr, &mac)) {
1832 // Provide Sender Information
1833 *saddr = mac;
1834 *sport = ntohs(sin.sin_port) - portOffset;
1835
1836 // Save Length
1837 *len = received; // Kurok homebrew seems to use the new value of len than returned value as data length
1838
1839 // Update last recv timestamp, may cause disconnection not detected properly tho
1840 peerlock.lock();
1841 auto peer = findFriend(&mac);
1842 if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
1843 peerlock.unlock();
1844
1845 // Free Network Lock
1846 //_freeNetworkLock();
1847
1848 // Return Success. According to pspsdk-1.0+beta2 returned value is Number of bytes received, but JPCSP returning 0?
1849 return 0; //received; // Returning number of bytes received will cause KH BBS unable to see the game event/room
1850 }
1851 // Unknown Peer, let's not give any data
1852 else {
1853 //*len = 0; // received; // Is this going to be okay since saddr may not be valid?
1854 WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %s:%u", id, getLocalPort(pdpsocket.id), received, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
1855 }
1856 // Free Network Lock
1857 //_freeNetworkLock();
1858
1859 //free(tmpbuf);
1860
1861 //Receiving data from unknown peer, ignore it ?
1862 //return ERROR_NET_ADHOC_WOULD_BLOCK; //ERROR_NET_ADHOC_NO_DATA_AVAILABLE
1863 }
1864
1865 // Free Network Lock
1866 //_freeNetworkLock();
1867
1868 #ifdef PDP_DIRTY_MAGIC
1869 // Restore Nonblocking Flag for Return Value
1870 if (wouldblock) flag = 1;
1871 #endif
1872
1873 DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Result:%i (Error:%i)", id, pdpsocket.lport, received, error);
1874
1875 // Timeout?
1876 return hleLogError(SCENET, ERROR_NET_ADHOC_TIMEOUT, "timeout");
1877 }
1878
1879 // Invalid Argument
1880 return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg");
1881 }
1882
1883 // Invalid Socket ID
1884 return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_SOCKET_ID, "invalid socket id");
1885 }
1886
1887 // Library is uninitialized
1888 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "not initialized");
1889 }
1890
NetAdhoc_SetSocketAlert(int id,s32_le flag)1891 int NetAdhoc_SetSocketAlert(int id, s32_le flag) {
1892 if (id < 1 || id > MAX_SOCKET || adhocSockets[id - 1] == NULL)
1893 return ERROR_NET_ADHOC_INVALID_SOCKET_ID;
1894
1895 // FIXME: Should we check for valid Alert Flags and/or Mask them? Should we return an error if we found an invalid flag?
1896 s32_le flg = flag & ADHOC_F_ALERTALL;
1897
1898 adhocSockets[id - 1]->flags = flg;
1899 adhocSockets[id - 1]->alerted_flags = 0;
1900
1901 return 0;
1902 }
1903
1904 // Flags seems to be bitmasks of ADHOC_F_ALERT... (need more games to test this)
sceNetAdhocSetSocketAlert(int id,int flag)1905 int sceNetAdhocSetSocketAlert(int id, int flag) {
1906 WARN_LOG_REPORT_ONCE(sceNetAdhocSetSocketAlert, SCENET, "UNTESTED sceNetAdhocSetSocketAlert(%d, %08x) at %08x", id, flag, currentMIPS->pc);
1907
1908 int retval = NetAdhoc_SetSocketAlert(id, flag);
1909 hleDelayResult(retval, "set socket alert delay", 1000);
1910 return hleLogDebug(SCENET, retval, "");
1911 }
1912
PollAdhocSocket(SceNetAdhocPollSd * sds,int count,int timeout,int nonblock)1913 int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout, int nonblock) {
1914 //WSAPoll only available for Vista or newer, so we'll use an alternative way for XP since Windows doesn't have poll function like *NIX
1915 fd_set readfds, writefds, exceptfds;
1916 int fd;
1917 int maxfd = 0;
1918 FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
1919
1920 for (int i = 0; i < count; i++) {
1921 sds[i].revents = 0;
1922 // Fill in Socket ID
1923 if (sds[i].id > 0 && sds[i].id <= MAX_SOCKET && adhocSockets[sds[i].id - 1] != NULL) {
1924 auto sock = adhocSockets[sds[i].id - 1];
1925 if (!sock) {
1926 return ERROR_NET_ADHOC_SOCKET_DELETED;
1927 }
1928 if (sock->type == SOCK_PTP) {
1929 fd = sock->data.ptp.id;
1930 }
1931 else {
1932 fd = sock->data.pdp.id;
1933 }
1934 if (fd > maxfd) maxfd = fd;
1935 FD_SET(fd, &readfds);
1936 FD_SET(fd, &writefds);
1937 FD_SET(fd, &exceptfds);
1938 }
1939 }
1940 timeval tmout;
1941 tmout.tv_sec = timeout / 1000000; // seconds
1942 tmout.tv_usec = (timeout % 1000000); // microseconds
1943 int affectedsockets = select(maxfd + 1, &readfds, &writefds, &exceptfds, &tmout);
1944 if (affectedsockets >= 0) {
1945 affectedsockets = 0;
1946 for (int i = 0; i < count; i++) {
1947 if (sds[i].id > 0 && sds[i].id <= MAX_SOCKET && adhocSockets[sds[i].id - 1] != NULL) {
1948 auto sock = adhocSockets[sds[i].id - 1];
1949 if (sock->type == SOCK_PTP) {
1950 fd = sock->data.ptp.id;
1951 }
1952 else {
1953 fd = sock->data.pdp.id;
1954 }
1955 if ((sds[i].events & ADHOC_EV_RECV) && FD_ISSET(fd, &readfds))
1956 sds[i].revents |= ADHOC_EV_RECV;
1957 if ((sds[i].events & ADHOC_EV_SEND) && FD_ISSET(fd, &writefds))
1958 sds[i].revents |= ADHOC_EV_SEND;
1959 if (sock->alerted_flags)
1960 sds[i].revents |= ADHOC_EV_ALERT;
1961 // Mask certain revents bits with events bits
1962 sds[i].revents &= sds[i].events;
1963
1964 if (sock->type == SOCK_PTP) {
1965 // FIXME: Should we also make use "retry_interval" for ADHOC_EV_ACCEPT, similar to ADHOC_EV_CONNECT ?
1966 if (sock->data.ptp.state == ADHOC_PTP_STATE_LISTEN && (sds[i].events & ADHOC_EV_ACCEPT) && FD_ISSET(fd, &readfds)) {
1967 sds[i].revents |= ADHOC_EV_ACCEPT;
1968 }
1969 // Fate Unlimited Codes and Carnage Heart EXA relies on AdhocPollSocket in order to retry a failed PtpConnect, but the interval must not be too long (about 1 frame before state became Established by GetPtpStat) for Bleach Heat the Soul 7 to work properly.
1970 else if ((sds[i].events & ADHOC_EV_CONNECT) && ((sock->data.ptp.state == ADHOC_PTP_STATE_CLOSED && sock->attemptCount == 0) ||
1971 (sock->data.ptp.state == ADHOC_PTP_STATE_SYN_SENT && (static_cast<s64>(CoreTiming::GetGlobalTimeUsScaled() - sock->lastAttempt) > 1000/*std::max(1000, sock->retry_interval - 60000)*/)))) {
1972
1973 sds[i].revents |= ADHOC_EV_CONNECT;
1974 }
1975 // Check for socket state (already disconnected/closed by remote peer, already closed/deleted, not a socket or not opened/connected yet?)
1976 // Raise ADHOC_EV_DISCONNECT, ADHOC_EV_DELETE, ADHOC_EV_INVALID on revents regardless of events as needed (similar to POLLHUP, POLLERR, and POLLNVAL on posix poll)
1977 if (sock->data.ptp.state == ADHOC_PTP_STATE_CLOSED) {
1978 if (sock->attemptCount > 0) {
1979 sds[i].revents |= ADHOC_EV_DISCONNECT; // remote peer has closed the socket
1980 }
1981 }
1982 }
1983
1984 if (sock->flags & ADHOC_F_ALERTPOLL) {
1985 sock->alerted_flags |= ADHOC_F_ALERTPOLL;
1986
1987 return ERROR_NET_ADHOC_SOCKET_ALERTED;
1988 }
1989 }
1990 else {
1991 sds[i].revents |= ADHOC_EV_INVALID;
1992 }
1993 if (sds[i].revents) affectedsockets++;
1994 }
1995 }
1996 else {
1997 // FIXME: Does AdhocPollSocket can return any error code other than ERROR_NET_ADHOC_EXCEPTION_EVENT on blocking/non-blocking mode?
1998 /*if (nonblock)
1999 affectedsockets = ERROR_NET_ADHOC_WOULD_BLOCK;
2000 else
2001 affectedsockets = ERROR_NET_ADHOC_TIMEOUT;
2002 */
2003 affectedsockets = ERROR_NET_ADHOC_EXCEPTION_EVENT;
2004 }
2005 return affectedsockets;
2006 }
2007
sceNetAdhocPollSocket(u32 socketStructAddr,int count,int timeout,int nonblock)2008 int sceNetAdhocPollSocket(u32 socketStructAddr, int count, int timeout, int nonblock) { // timeout in microseconds
2009 DEBUG_LOG_REPORT_ONCE(sceNetAdhocPollSocket, SCENET, "UNTESTED sceNetAdhocPollSocket(%08x, %i, %i, %i) at %08x", socketStructAddr, count, timeout, nonblock, currentMIPS->pc);
2010 // Library is initialized
2011 if (netAdhocInited)
2012 {
2013 SceNetAdhocPollSd * sds = NULL;
2014 if (Memory::IsValidAddress(socketStructAddr)) sds = (SceNetAdhocPollSd *)Memory::GetPointer(socketStructAddr);
2015
2016 // Valid Arguments
2017 if (sds != NULL && count > 0)
2018 {
2019 // Socket Check
2020 for (int i = 0; i < count; i++) {
2021 // Invalid Socket
2022 if (sds[i].id < 1 || sds[i].id > MAX_SOCKET || adhocSockets[sds[i].id - 1] == NULL)
2023 return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_SOCKET_ID, "invalid socket id");
2024 }
2025
2026 // Nonblocking Mode
2027 if (nonblock)
2028 timeout = 0;
2029
2030 if (count > (int)FD_SETSIZE)
2031 count = FD_SETSIZE; // return 0; //ERROR_NET_ADHOC_INVALID_ARG
2032
2033 // Acquire Network Lock
2034 //acquireNetworkLock();
2035
2036 // Poll Sockets
2037 //int affectedsockets = sceNetInetPoll(isds, count, timeout);
2038 int affectedsockets = 0;
2039 if (nonblock)
2040 affectedsockets = PollAdhocSocket(sds, count, 0, nonblock);
2041 else {
2042 // Simulate blocking behaviour with non-blocking socket
2043 // Borrowing some arguments to pass some parameters. The dummy WaitID(count+1) might not be unique thus have duplicate possibilities if there are multiple thread trying to poll the same numbers of socket at the same time
2044 u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | (count + 1ULL);
2045 return WaitBlockingAdhocSocket(threadSocketId, ADHOC_POLL_SOCKET, count, sds, nullptr, timeout, nullptr, nullptr, "adhoc pollsocket");
2046 }
2047
2048 // Free Network Lock
2049 //freeNetworkLock();
2050
2051 // No Events generated
2052 // Non-blocking mode
2053 // Bleach 7 seems to use nonblocking and check the return value > 0, or 0x80410709 (ERROR_NET_ADHOC_WOULD_BLOCK), also 0x80410717 (ERROR_NET_ADHOC_EXCEPTION_EVENT), when using prx files on JPCSP it can return 0
2054 if (affectedsockets >= 0) {
2055 if (affectedsockets > 0) {
2056 for (int i = 0; i < count; i++) {
2057 if (sds[i].id > 0 && sds[i].id <= MAX_SOCKET && adhocSockets[sds[i].id - 1] != NULL) {
2058 auto sock = adhocSockets[sds[i].id - 1];
2059 if (sock->type == SOCK_PTP)
2060 VERBOSE_LOG(SCENET, "Poll PTP Socket Id: %d (%d), events: %08x, revents: %08x - state: %d", sds[i].id, sock->data.ptp.id, sds[i].events, sds[i].revents, sock->data.ptp.state);
2061 else
2062 VERBOSE_LOG(SCENET, "Poll PDP Socket Id: %d (%d), events: %08x, revents: %08x", sds[i].id, sock->data.pdp.id, sds[i].events, sds[i].revents);
2063 }
2064 }
2065 }
2066 // Workaround to get 30 FPS instead of the too fast 60 FPS on Fate Unlimited Codes, it's abit absurd for a non-blocking call to have this much delay tho, and hleDelayResult doesn't works as good as hleEatMicro for this workaround.
2067 hleEatMicro(50); // hleEatMicro(7500); // normally 1ms, but using 7.5ms here seems to show better result for Bleach Heat the Soul 7 and other games with too high FPS, but may have a risk of slowing down games that already runs at normal FPS? (need more games to test this)
2068 return hleLogDebug(SCENET, affectedsockets, "success");
2069 }
2070 //else if (nonblock && affectedsockets < 0)
2071 // return hleLogDebug(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block"); // Is this error code valid for PollSocket? as it always returns 0 even when nonblock flag is set
2072
2073 return hleLogDebug(SCENET, ERROR_NET_ADHOC_EXCEPTION_EVENT, "exception event");
2074 }
2075
2076 // Invalid Argument
2077 return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg");
2078 }
2079
2080 // Library is uninitialized
2081 return hleLogDebug(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "adhoc not initialized");
2082 }
2083
NetAdhocPdp_Delete(int id,int unknown)2084 int NetAdhocPdp_Delete(int id, int unknown) {
2085 // Library is initialized
2086 if (netAdhocInited) {
2087 // Valid Arguments
2088 if (id > 0 && id <= MAX_SOCKET) {
2089 // Cast Socket
2090 auto sock = adhocSockets[id - 1];
2091
2092 // Valid Socket
2093 if (sock != NULL && sock->type == SOCK_PDP) {
2094 // Close Connection
2095 struct linger sl;
2096 sl.l_onoff = 1; // non-zero value enables linger option in kernel
2097 sl.l_linger = 0; // timeout interval in seconds
2098 setsockopt(sock->data.pdp.id, SOL_SOCKET, SO_LINGER, (const char*)&sl, sizeof(sl));
2099 shutdown(sock->data.pdp.id, SD_BOTH);
2100 closesocket(sock->data.pdp.id);
2101
2102 // Remove Port Forward from Router
2103 //sceNetPortClose("UDP", sock->lport);
2104 //g_PortManager.Remove(IP_PROTOCOL_UDP, isOriPort ? sock->lport : sock->lport + portOffset); // Let's not remove mapping in real-time as it could cause lags/disconnection when joining a room with slow routers
2105
2106 // Free Memory
2107 free(sock);
2108
2109 // Free Translation Slot
2110 adhocSockets[id - 1] = NULL;
2111
2112 // Success
2113 return 0;
2114 }
2115
2116 // Invalid Socket ID
2117 return ERROR_NET_ADHOC_INVALID_SOCKET_ID;
2118 }
2119
2120 // Invalid Argument
2121 return ERROR_NET_ADHOC_INVALID_ARG;
2122 }
2123
2124 // Library is uninitialized
2125 return ERROR_NET_ADHOC_NOT_INITIALIZED;
2126 }
2127
2128 /**
2129 * Adhoc Emulator PDP Socket Delete
2130 * @param id Socket File Descriptor
2131 * @param flag Bitflags (Unused)
2132 * @return 0 on success or... ADHOC_INVALID_ARG, ADHOC_NOT_INITIALIZED, ADHOC_INVALID_SOCKET_ID
2133 */
sceNetAdhocPdpDelete(int id,int unknown)2134 static int sceNetAdhocPdpDelete(int id, int unknown) {
2135 // WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right?
2136 INFO_LOG(SCENET, "sceNetAdhocPdpDelete(%d, %d) at %08x", id, unknown, currentMIPS->pc);
2137 /*
2138 if (!g_Config.bEnableWlan) {
2139 return 0;
2140 }
2141 */
2142
2143 return NetAdhocPdp_Delete(id, unknown);
2144 }
2145
sceNetAdhocctlGetAdhocId(u32 productStructAddr)2146 static int sceNetAdhocctlGetAdhocId(u32 productStructAddr) {
2147 INFO_LOG(SCENET, "sceNetAdhocctlGetAdhocId(%08x) at %08x", productStructAddr, currentMIPS->pc);
2148
2149 // Library initialized
2150 if (netAdhocctlInited)
2151 {
2152 // Valid Arguments
2153 if (Memory::IsValidAddress(productStructAddr))
2154 {
2155 // Copy Product ID
2156 Memory::WriteStruct(productStructAddr, &product_code);
2157
2158 // Return Success
2159 return hleLogDebug(SCENET, 0, "type = %d, code = %s", product_code.type, product_code.data);
2160 }
2161
2162 // Invalid Arguments
2163 return hleLogDebug(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg");
2164 }
2165
2166 // Library uninitialized
2167 return hleLogDebug(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized");
2168 }
2169
2170 // FIXME: Scan probably not a blocking function since there is ADHOCCTL_STATE_SCANNING state that can be polled by the game, right? But apparently it need to be delayed for Naruto Shippuden Ultimate Ninja Heroes 3
sceNetAdhocctlScan()2171 int sceNetAdhocctlScan() {
2172 INFO_LOG(SCENET, "sceNetAdhocctlScan() at %08x", currentMIPS->pc);
2173 if (!g_Config.bEnableWlan) {
2174 return -1;
2175 }
2176
2177 // Library initialized
2178 if (netAdhocctlInited) {
2179 int us = adhocDefaultDelay;
2180 // FIXME: When tested with JPCSP + official prx files it seems when adhocctl in a connected state (ie. joined to a group) attempting to create/connect/join/scan will return a success (without doing anything?)
2181 if ((adhocctlState == ADHOCCTL_STATE_CONNECTED) || (adhocctlState == ADHOCCTL_STATE_GAMEMODE)) {
2182 // TODO: Valhalla Knights 2 need handler notification, but need to test this on games that doesn't use Adhocctl Handler too (not sure if there are games like that tho)
2183 notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, ERROR_NET_ADHOCCTL_ALREADY_CONNECTED);
2184 hleEatMicro(500);
2185 return 0;
2186 }
2187
2188 // Only scan when in Disconnected state, otherwise AdhocServer will kick you out
2189 if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED && !isAdhocctlBusy) {
2190 isAdhocctlBusy = true;
2191 isAdhocctlNeedLogin = true;
2192 adhocctlState = ADHOCCTL_STATE_SCANNING;
2193 adhocctlCurrentMode = ADHOCCTL_MODE_NORMAL;
2194
2195 // Reset Networks/Group list to prevent other threads from using these soon to be replaced networks
2196 peerlock.lock();
2197 freeGroupsRecursive(networks);
2198 networks = NULL;
2199 peerlock.unlock();
2200
2201 if (friendFinderRunning) {
2202 AdhocctlRequest req = { OPCODE_SCAN, {0} };
2203 return WaitBlockingAdhocctlSocket(req, us, "adhocctl scan");
2204 }
2205 else {
2206 adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
2207 }
2208
2209 // Return Success and let friendFinder thread to notify the handler when scan completed
2210 // Not delaying here may cause Naruto Shippuden Ultimate Ninja Heroes 3 to get disconnected when the mission started
2211 hleEatMicro(us);
2212 // FIXME: When tested using JPCSP + official prx files it seems sceNetAdhocctlScan switching to a different thread for at least 100ms after returning success and before executing the next line?
2213 return hleDelayResult(0, "scan delay", adhocEventPollDelay);
2214 }
2215
2216 // FIXME: Returning BUSY when previous adhocctl handler's callback is not fully executed yet, But returning success and notifying handler's callback with error (ie. ALREADY_CONNECTED) when previous adhocctl handler's callback is fully executed? Is there a case where error = BUSY sent through handler's callback?
2217 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_BUSY, "busy");
2218 }
2219
2220 // Library uninitialized
2221 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized");
2222 }
2223
sceNetAdhocctlGetScanInfo(u32 sizeAddr,u32 bufAddr)2224 int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) {
2225 s32_le *buflen = NULL;
2226 if (Memory::IsValidAddress(sizeAddr)) buflen = (s32_le *)Memory::GetPointer(sizeAddr);
2227 SceNetAdhocctlScanInfoEmu *buf = NULL;
2228 if (Memory::IsValidAddress(bufAddr)) buf = (SceNetAdhocctlScanInfoEmu *)Memory::GetPointer(bufAddr);
2229
2230 INFO_LOG(SCENET, "sceNetAdhocctlGetScanInfo([%08x]=%i, %08x) at %08x", sizeAddr, Memory::Read_U32(sizeAddr), bufAddr, currentMIPS->pc);
2231 if (!g_Config.bEnableWlan) {
2232 return 0;
2233 }
2234
2235 // Library initialized
2236 if (netAdhocctlInited) {
2237 // Minimum Argument
2238 if (buflen == NULL) return ERROR_NET_ADHOCCTL_INVALID_ARG;
2239
2240 // Minimum Argument Requirements
2241 if (buflen != NULL) {
2242 // FIXME: Do we need to exclude Groups created by this device it's self?
2243 bool excludeSelf = false;
2244
2245 // Multithreading Lock
2246 peerlock.lock();
2247
2248 // FIXME: When already connected to a group GetScanInfo will return size = 0 ? or may be only hides the group created by it's self?
2249 if (adhocctlState == ADHOCCTL_STATE_CONNECTED || adhocctlState == ADHOCCTL_STATE_GAMEMODE) {
2250 *buflen = 0;
2251 DEBUG_LOG(SCENET, "NetworkList [Available: 0] Already in a Group");
2252 }
2253 // Length Returner Mode
2254 else if (buf == NULL) {
2255 int availNetworks = countAvailableNetworks(excludeSelf);
2256 *buflen = availNetworks * sizeof(SceNetAdhocctlScanInfoEmu);
2257 DEBUG_LOG(SCENET, "NetworkList [Available: %i]", availNetworks);
2258 }
2259 // Normal Information Mode
2260 else {
2261 // Clear Memory
2262 memset(buf, 0, *buflen);
2263
2264 // Network Discovery Counter
2265 int discovered = 0;
2266
2267 // Count requested Networks
2268 int requestcount = *buflen / sizeof(SceNetAdhocctlScanInfoEmu);
2269
2270 // Minimum Argument Requirements
2271 if (requestcount > 0) {
2272 // Group List Element
2273 SceNetAdhocctlScanInfo * group = networks;
2274
2275 // Iterate Group List
2276 for (; group != NULL && (!excludeSelf || !isLocalMAC(&group->bssid.mac_addr)) && discovered < requestcount; group = group->next) {
2277 // Copy Group Information
2278 //buf[discovered] = *group;
2279 buf[discovered].group_name = group->group_name;
2280 buf[discovered].bssid = group->bssid;
2281 buf[discovered].mode = group->mode;
2282
2283 // Exchange Adhoc Channel
2284 // sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_ADHOC_CHANNEL, &buf[discovered].channel);
2285
2286 // Fake Channel Number 1 on Automatic Channel (JPCSP use 11 as default). Ridge Racer 2 will ignore any groups with channel 0 or that doesn't matched with channel value returned from sceUtilityGetSystemParamInt (which mean sceUtilityGetSystemParamInt must not return channel 0 when connected to a network?)
2287 buf[discovered].channel = group->channel; //parameter.channel
2288
2289 // Increase Discovery Counter
2290 discovered++;
2291 }
2292
2293 // Link List
2294 for (int i = 0; i < discovered - 1; i++) {
2295 // Link Network
2296 buf[i].next = bufAddr + (sizeof(SceNetAdhocctlScanInfoEmu)*i) + sizeof(SceNetAdhocctlScanInfoEmu); // buf[i].next = &buf[i + 1];
2297 }
2298
2299 // Fix Last Element
2300 if (discovered > 0) buf[discovered - 1].next = 0;
2301 }
2302
2303 // Fix Size
2304 *buflen = discovered * sizeof(SceNetAdhocctlScanInfoEmu);
2305 DEBUG_LOG(SCENET, "NetworkList [Requested: %i][Discovered: %i]", requestcount, discovered);
2306 }
2307
2308 // Multithreading Unlock
2309 peerlock.unlock();
2310
2311 hleEatMicro(200);
2312 // Return Success
2313 return 0;
2314 }
2315
2316 // Generic Error
2317 return -1;
2318 }
2319
2320 // Library uninitialized
2321 return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
2322 }
2323
2324 // TODO: How many handlers can the PSP actually have for Adhocctl?
2325 // TODO: Should we allow the same handler to be added more than once?
2326 // FIXME: Do all Adhocctl HLE returning 0 and expecting error code through callback handler if there were error, instead of returning error code through the HLE ?
sceNetAdhocctlAddHandler(u32 handlerPtr,u32 handlerArg)2327 static u32 sceNetAdhocctlAddHandler(u32 handlerPtr, u32 handlerArg) {
2328 bool foundHandler = false;
2329 u32 retval = 0;
2330 AdhocctlHandler handler;
2331 memset(&handler, 0, sizeof(handler));
2332
2333 while (adhocctlHandlers.find(retval) != adhocctlHandlers.end())
2334 ++retval;
2335
2336 handler.entryPoint = handlerPtr;
2337 handler.argument = handlerArg;
2338
2339 for (auto it = adhocctlHandlers.begin(); it != adhocctlHandlers.end(); ++it) {
2340 if (it->second.entryPoint == handlerPtr) {
2341 foundHandler = true;
2342 break;
2343 }
2344 }
2345
2346 if (!foundHandler && Memory::IsValidAddress(handlerPtr)) {
2347 if (adhocctlHandlers.size() >= MAX_ADHOCCTL_HANDLERS) {
2348 ERROR_LOG(SCENET, "UNTESTED sceNetAdhocctlAddHandler(%x, %x): Too many handlers", handlerPtr, handlerArg);
2349 retval = ERROR_NET_ADHOCCTL_TOO_MANY_HANDLERS;
2350 return retval;
2351 }
2352 adhocctlHandlers[retval] = handler;
2353 WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlAddHandler(%x, %x): added handler %d", handlerPtr, handlerArg, retval);
2354 } else if(foundHandler) {
2355 ERROR_LOG(SCENET, "UNTESTED sceNetAdhocctlAddHandler(%x, %x): Same handler already exists", handlerPtr, handlerArg);
2356 retval = 0; //Faking success
2357 } else {
2358 ERROR_LOG(SCENET, "UNTESTED sceNetAdhocctlAddHandler(%x, %x): Invalid handler", handlerPtr, handlerArg);
2359 retval = ERROR_NET_ADHOCCTL_INVALID_ARG;
2360 }
2361
2362 // The id to return is the number of handlers currently registered
2363 return retval;
2364 }
2365
NetAdhocctl_Disconnect()2366 u32 NetAdhocctl_Disconnect() {
2367 // Library initialized
2368 if (netAdhocctlInited) {
2369 int iResult, error;
2370 int us = adhocDefaultDelay * 3;
2371 hleEatMicro(1000);
2372
2373 if (isAdhocctlBusy && CoreTiming::IsScheduled(adhocctlNotifyEvent)) {
2374 return ERROR_NET_ADHOCCTL_BUSY;
2375 }
2376
2377 // Connected State (Adhoc Mode). Attempting to leave a group while not in a group will be kicked out by Adhoc Server (ie. some games tries to disconnect more than once within a short time)
2378 if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED) {
2379 isAdhocctlBusy = true;
2380
2381 // Clear Network Name
2382 memset(¶meter.group_name, 0, sizeof(parameter.group_name));
2383
2384 // Set HUD Connection Status
2385 //setConnectionStatus(0);
2386
2387 // Prepare Packet
2388 uint8_t opcode = OPCODE_DISCONNECT;
2389
2390 // Acquire Network Lock
2391 //_acquireNetworkLock();
2392
2393 // Send Disconnect Request Packet
2394 iResult = send((int)metasocket, (const char*)&opcode, 1, MSG_NOSIGNAL);
2395 error = errno;
2396
2397 // Sending may get socket error 10053 if the AdhocServer is already shutted down
2398 if (iResult == SOCKET_ERROR) {
2399 if (error != EAGAIN && error != EWOULDBLOCK) {
2400 ERROR_LOG(SCENET, "Socket error (%i) when sending", error);
2401 // Set Disconnected State
2402 adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
2403 }
2404 else if (friendFinderRunning) {
2405 AdhocctlRequest req = { OPCODE_DISCONNECT, {0} };
2406 WaitBlockingAdhocctlSocket(req, us, "adhocctl disconnect");
2407 }
2408 else {
2409 // Set Disconnected State
2410 return ERROR_NET_ADHOCCTL_BUSY;
2411 }
2412 }
2413
2414 // Free Network Lock
2415 //_freeNetworkLock();
2416 }
2417
2418 // Multithreading Lock
2419 //peerlock.lock();
2420
2421 // Clear Peer List, since games are moving to a different a group when the mission started may be we shouldn't free all peers yet
2422 int32_t peercount = 0;
2423 timeoutFriendsRecursive(friends, &peercount);
2424 INFO_LOG(SCENET, "Marked for Timedout Peer List (%i)", peercount);
2425 // Delete Peer Reference
2426 //friends = NULL;
2427
2428 // Clear Group List
2429 //freeGroupsRecursive(networks);
2430
2431 // Delete Group Reference
2432 //networks = NULL;
2433
2434 // Multithreading Unlock
2435 //peerlock.unlock();
2436
2437 adhocctlCurrentMode = ADHOCCTL_MODE_NONE;
2438 // Notify Event Handlers (even if we weren't connected, not doing this will freeze games like God Eater, which expect this behaviour)
2439 // FIXME: When there are no handler the state will immediately became ADHOCCTL_STATE_DISCONNECTED ?
2440 if (adhocctlHandlers.empty()) {
2441 adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
2442 }
2443 else {
2444 notifyAdhocctlHandlers(ADHOCCTL_EVENT_DISCONNECT, 0);
2445 }
2446
2447 // Return Success, some games might ignore returned value and always treat it as success, otherwise repeatedly calling this function
2448 return 0;
2449 }
2450
2451 // Library uninitialized
2452 return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
2453 }
2454
sceNetAdhocctlDisconnect()2455 static u32 sceNetAdhocctlDisconnect() {
2456 // WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right?
2457 char grpName[9] = { 0 };
2458 memcpy(grpName, parameter.group_name.data, ADHOCCTL_GROUPNAME_LEN); // Copied to null-terminated var to prevent unexpected behaviour on Logs
2459 int ret = NetAdhocctl_Disconnect();
2460 INFO_LOG(SCENET, "%08x=sceNetAdhocctlDisconnect() at %08x [group=%s]", ret, currentMIPS->pc, grpName);
2461
2462 return ret;
2463 }
2464
sceNetAdhocctlDelHandler(u32 handlerID)2465 static u32 sceNetAdhocctlDelHandler(u32 handlerID) {
2466 if (!netAdhocctlInited)
2467 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "adhocctl not initialized");
2468
2469 if (adhocctlHandlers.find(handlerID) != adhocctlHandlers.end()) {
2470 adhocctlHandlers.erase(handlerID);
2471 INFO_LOG(SCENET, "sceNetAdhocctlDelHandler(%d) at %08x", handlerID, currentMIPS->pc);
2472 } else {
2473 WARN_LOG(SCENET, "sceNetAdhocctlDelHandler(%d): Invalid Handler ID", handlerID);
2474 }
2475
2476 return 0;
2477 }
2478
NetAdhocctl_Term()2479 int NetAdhocctl_Term() {
2480 if (netAdhocctlInited) {
2481 if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED) {
2482 // Note: This might block current thread if the first attempt to send OPCODE_DISCONNECT to AdhocServer failed with EAGAIN error
2483 if (netAdhocGameModeEntered)
2484 NetAdhocctl_ExitGameMode();
2485 else
2486 NetAdhocctl_Disconnect();
2487 }
2488
2489 // Terminate Adhoc Threads
2490 friendFinderRunning = false;
2491 if (friendFinderThread.joinable()) {
2492 friendFinderThread.join();
2493 }
2494
2495 // TODO: May need to block current thread to make sure all Adhocctl callbacks have been fully executed before terminating Adhoc PSPThread (ie. threadAdhocID).
2496
2497 // Clear GameMode resources
2498 NetAdhocGameMode_DeleteMaster();
2499 deleteAllGMB();
2500
2501 // Clear Peer List
2502 int32_t peercount = 0;
2503 freeFriendsRecursive(friends, &peercount);
2504 INFO_LOG(SCENET, "Cleared Peer List (%i)", peercount);
2505 // Delete Peer Reference
2506 friends = NULL;
2507 //May also need to clear Handlers
2508 adhocctlHandlers.clear();
2509 // Free stuff here
2510 networkInited = false;
2511 shutdown((int)metasocket, SD_BOTH);
2512 closesocket((int)metasocket);
2513 metasocket = (int)INVALID_SOCKET;
2514 // Delete fake PSP Thread.
2515 // kernelObjects may already been cleared early during a Shutdown, thus trying to access it may generates Warning/Error in the log
2516 if (threadAdhocID > 0 && strcmp(__KernelGetThreadName(threadAdhocID), "ERROR") != 0) {
2517 __KernelStopThread(threadAdhocID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocThread stopped");
2518 __KernelDeleteThread(threadAdhocID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocThread deleted");
2519 }
2520 threadAdhocID = 0;
2521 adhocctlCurrentMode = ADHOCCTL_MODE_NONE;
2522 isAdhocctlBusy = false;
2523 netAdhocctlInited = false;
2524 }
2525
2526 return 0;
2527 }
2528
sceNetAdhocctlTerm()2529 int sceNetAdhocctlTerm() {
2530 // WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right?
2531 INFO_LOG(SCENET, "sceNetAdhocctlTerm() at %08x", currentMIPS->pc);
2532
2533 //if (netAdhocMatchingInited) NetAdhocMatching_Term();
2534 int retval = NetAdhocctl_Term();
2535
2536 hleEatMicro(adhocDefaultDelay);
2537 return retval;
2538 }
2539
sceNetAdhocctlGetNameByAddr(const char * mac,u32 nameAddr)2540 static int sceNetAdhocctlGetNameByAddr(const char *mac, u32 nameAddr) {
2541 DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocctlGetNameByAddr(%s, %08x) at %08x", mac2str((SceNetEtherAddr*)mac).c_str(), nameAddr, currentMIPS->pc);
2542
2543 // Library initialized
2544 if (netAdhocctlInited)
2545 {
2546 // Valid Arguments
2547 if (mac != NULL && Memory::IsValidAddress(nameAddr))
2548 {
2549 SceNetAdhocctlNickname * nickname = (SceNetAdhocctlNickname *)Memory::GetPointer(nameAddr);
2550 // Get Local MAC Address
2551 SceNetEtherAddr localmac;
2552 getLocalMac(&localmac);
2553
2554 // Local MAC Matches
2555 if (isMacMatch(&localmac, (const SceNetEtherAddr*)mac))
2556 {
2557 // Write Data
2558 *nickname = parameter.nickname;
2559
2560 DEBUG_LOG(SCENET, "sceNetAdhocctlGetNameByAddr - [PlayerName:%s]", (char*)nickname);
2561
2562 // Return Success
2563 return 0;
2564 }
2565
2566 // Multithreading Lock
2567 peerlock.lock();
2568
2569 // Peer Reference
2570 SceNetAdhocctlPeerInfo * peer = friends;
2571
2572 // Iterate Peers
2573 for (; peer != NULL; peer = peer->next)
2574 {
2575 // Match found
2576 if (peer->last_recv != 0 && isMacMatch(&peer->mac_addr, (const SceNetEtherAddr*)mac))
2577 {
2578 // Write Data
2579 *nickname = peer->nickname;
2580
2581 // Multithreading Unlock
2582 peerlock.unlock();
2583
2584 DEBUG_LOG(SCENET, "sceNetAdhocctlGetNameByAddr - [PeerName:%s]", (char*)nickname);
2585
2586 // Return Success
2587 return 0;
2588 }
2589 }
2590
2591 // Multithreading Unlock
2592 peerlock.unlock();
2593
2594 DEBUG_LOG(SCENET, "sceNetAdhocctlGetNameByAddr - PlayerName not found");
2595 // Player not found
2596 return ERROR_NET_ADHOC_NO_ENTRY;
2597 }
2598
2599 // Invalid Arguments
2600 return ERROR_NET_ADHOCCTL_INVALID_ARG;
2601 }
2602
2603 // Library uninitialized
2604 return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
2605 }
2606
sceNetAdhocctlGetPeerInfo(const char * mac,int size,u32 peerInfoAddr)2607 int sceNetAdhocctlGetPeerInfo(const char *mac, int size, u32 peerInfoAddr) {
2608 VERBOSE_LOG(SCENET, "sceNetAdhocctlGetPeerInfo(%s, %i, %08x) at %08x", mac2str((SceNetEtherAddr*)mac).c_str(), size, peerInfoAddr, currentMIPS->pc);
2609 if (!g_Config.bEnableWlan) {
2610 return -1;
2611 }
2612
2613 SceNetEtherAddr * maddr = (SceNetEtherAddr *)mac;
2614 SceNetAdhocctlPeerInfoEmu * buf = NULL;
2615 if (Memory::IsValidAddress(peerInfoAddr)) {
2616 buf = (SceNetAdhocctlPeerInfoEmu *)Memory::GetPointer(peerInfoAddr);
2617 }
2618 // Library initialized
2619 if (netAdhocctlInited) {
2620 if ((size < (int)sizeof(SceNetAdhocctlPeerInfoEmu)) || (buf == NULL)) return ERROR_NET_ADHOCCTL_INVALID_ARG;
2621
2622 int retval = ERROR_NET_ADHOC_NO_ENTRY; // -1;
2623
2624 // Local MAC
2625 if (isLocalMAC(maddr)) {
2626 SceNetAdhocctlNickname nickname;
2627
2628 truncate_cpy((char*)&nickname.data, ADHOCCTL_NICKNAME_LEN, g_Config.sNickName.c_str());
2629 buf->next = 0;
2630 buf->nickname = nickname;
2631 buf->nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char
2632 buf->mac_addr = *maddr;
2633 buf->flags = 0x0400;
2634 buf->padding = 0;
2635 buf->last_recv = std::max(0LL, static_cast<s64>(CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta));
2636
2637 // Success
2638 retval = 0;
2639 }
2640 // Find Peer by MAC
2641 else
2642 {
2643 // Multithreading Lock
2644 peerlock.lock();
2645
2646 SceNetAdhocctlPeerInfo * peer = findFriend(maddr);
2647 if (peer != NULL && peer->last_recv != 0) {
2648 // Fake Receive Time
2649 peer->last_recv = std::max(peer->last_recv, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta);
2650
2651 buf->next = 0;
2652 buf->nickname = peer->nickname;
2653 buf->nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char
2654 buf->mac_addr = *maddr;
2655 buf->flags = 0x0400; //peer->ip_addr;
2656 buf->padding = 0;
2657 buf->last_recv = peer->last_recv; //CoreTiming::GetGlobalTimeUsScaled(); //time_now_d()*1000000.0; //(uint64_t)time(NULL); //This timestamp is important issue on Dissidia 012
2658
2659 // Success
2660 retval = 0;
2661 }
2662
2663 // Multithreading Unlock
2664 peerlock.unlock();
2665 }
2666 hleEatMicro(50);
2667 return retval;
2668 }
2669
2670 // Library uninitialized
2671 return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
2672 }
2673
NetAdhocctl_Create(const char * groupName)2674 int NetAdhocctl_Create(const char* groupName) {
2675 const SceNetAdhocctlGroupName* groupNameStruct = (const SceNetAdhocctlGroupName*)groupName;
2676 // Library initialized
2677 if (netAdhocctlInited) {
2678 // Valid Argument
2679 if (validNetworkName(groupNameStruct)) {
2680 // FIXME: When tested with JPCSP + official prx files it seems when adhocctl in a connected state (ie. joined to a group) attempting to create/connect/join/scan will return a success (without doing anything?)
2681 if ((adhocctlState == ADHOCCTL_STATE_CONNECTED) || (adhocctlState == ADHOCCTL_STATE_GAMEMODE)) {
2682 // TODO: Need to test this on games that doesn't use Adhocctl Handler too (not sure if there are games like that tho)
2683 notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, ERROR_NET_ADHOCCTL_ALREADY_CONNECTED);
2684 hleEatMicro(500);
2685 return 0;
2686 }
2687
2688 // Disconnected State
2689 if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED && !isAdhocctlBusy) {
2690 isAdhocctlBusy = true;
2691 isAdhocctlNeedLogin = true;
2692
2693 // Set Network Name
2694 if (groupNameStruct != NULL)
2695 parameter.group_name = *groupNameStruct;
2696
2697 // Reset Network Name
2698 else
2699 memset(¶meter.group_name, 0, sizeof(parameter.group_name));
2700
2701 // Set HUD Connection Status
2702 //setConnectionStatus(1);
2703
2704 // Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session
2705 int us = adhocDefaultDelay;
2706 if (friendFinderRunning) {
2707 AdhocctlRequest req = { OPCODE_CONNECT, parameter.group_name };
2708 return WaitBlockingAdhocctlSocket(req, us, "adhocctl connect");
2709 }
2710 //Faking success, to prevent Full Auto 2 from freezing while Initializing Network
2711 else {
2712 adhocctlStartTime = (u64)(time_now_d() * 1000000.0);
2713 if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {
2714 adhocctlState = ADHOCCTL_STATE_GAMEMODE;
2715 notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);
2716 }
2717 else {
2718 adhocctlState = ADHOCCTL_STATE_CONNECTED;
2719 // Notify Event Handlers, Needed for the Nickname to be shown on the screen when success is faked
2720 // Connected Event's mipscall need be executed before returning from sceNetAdhocctlCreate (or before the next sceNet function?)
2721 notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); //CoreTiming::ScheduleEvent_Threadsafe_Immediate(eventAdhocctlHandlerUpdate, join32(ADHOCCTL_EVENT_CONNECT, 0));
2722 }
2723 }
2724
2725 hleEatMicro(us);
2726 // Return Success
2727 // FIXME: When tested using JPCSP + official prx files it seems sceNetAdhocctlCreate switching to a different thread for at least 100ms after returning success and before executing the next line.
2728 return 0;
2729 }
2730
2731 // Connected State
2732 return ERROR_NET_ADHOCCTL_BUSY; // ERROR_NET_ADHOCCTL_BUSY may trigger the game (ie. Ford Street Racing) to call sceNetAdhocctlDisconnect
2733 }
2734
2735 // Invalid Argument
2736 return ERROR_NET_ADHOC_INVALID_ARG;
2737 }
2738 // Library uninitialized
2739 return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
2740 }
2741
2742 /**
2743 * Create and / or Join a Virtual Network of the specified Name
2744 * @param group_name Virtual Network Name
2745 * @return 0 on success or... ADHOCCTL_NOT_INITIALIZED, ADHOCCTL_INVALID_ARG, ADHOCCTL_BUSY
2746 */
sceNetAdhocctlCreate(const char * groupName)2747 int sceNetAdhocctlCreate(const char *groupName) {
2748 char grpName[ADHOCCTL_GROUPNAME_LEN + 1] = { 0 };
2749 if (groupName)
2750 memcpy(grpName, groupName, ADHOCCTL_GROUPNAME_LEN); // For logging purpose, must not be truncated
2751 INFO_LOG(SCENET, "sceNetAdhocctlCreate(%s) at %08x", grpName, currentMIPS->pc);
2752 if (!g_Config.bEnableWlan) {
2753 return -1;
2754 }
2755
2756 adhocctlCurrentMode = ADHOCCTL_MODE_NORMAL;
2757 adhocConnectionType = ADHOC_CREATE;
2758 return hleLogDebug(SCENET, NetAdhocctl_Create(groupName), "");
2759 }
2760
sceNetAdhocctlConnect(const char * groupName)2761 int sceNetAdhocctlConnect(const char* groupName) {
2762 char grpName[ADHOCCTL_GROUPNAME_LEN + 1] = { 0 };
2763 if (groupName)
2764 memcpy(grpName, groupName, ADHOCCTL_GROUPNAME_LEN); // For logging purpose, must not be truncated
2765 INFO_LOG(SCENET, "sceNetAdhocctlConnect(%s) at %08x", grpName, currentMIPS->pc);
2766 if (!g_Config.bEnableWlan) {
2767 return -1;
2768 }
2769
2770 adhocctlCurrentMode = ADHOCCTL_MODE_NORMAL;
2771 adhocConnectionType = ADHOC_CONNECT;
2772 return hleLogDebug(SCENET, NetAdhocctl_Create(groupName), "");
2773 }
2774
sceNetAdhocctlJoin(u32 scanInfoAddr)2775 int sceNetAdhocctlJoin(u32 scanInfoAddr) {
2776 INFO_LOG(SCENET, "sceNetAdhocctlJoin(%08x) at %08x", scanInfoAddr, currentMIPS->pc);
2777 if (!g_Config.bEnableWlan) {
2778 return -1;
2779 }
2780
2781 // Library initialized
2782 if (netAdhocctlInited)
2783 {
2784 // Valid Argument
2785 if (Memory::IsValidAddress(scanInfoAddr))
2786 {
2787 SceNetAdhocctlScanInfoEmu* sinfo = (SceNetAdhocctlScanInfoEmu*)Memory::GetPointer(scanInfoAddr);
2788 char grpName[ADHOCCTL_GROUPNAME_LEN + 1] = { 0 };
2789 memcpy(grpName, sinfo->group_name.data, ADHOCCTL_GROUPNAME_LEN); // For logging purpose, must not be truncated
2790 DEBUG_LOG(SCENET, "sceNetAdhocctlJoin - Group: %s", grpName);
2791
2792 // We can ignore minor connection process differences here
2793 // TODO: Adhoc Server may need to be changed to differentiate between Host/Create and Join, otherwise it can't support multiple Host using the same Group name, thus causing one of the Host to be confused being treated as Join.
2794 adhocctlCurrentMode = ADHOCCTL_MODE_NORMAL;
2795 adhocConnectionType = ADHOC_JOIN;
2796 return hleLogDebug(SCENET, NetAdhocctl_Create(grpName), "");
2797 }
2798
2799 // Invalid Argument
2800 return ERROR_NET_ADHOCCTL_INVALID_ARG;
2801 }
2802
2803 // Uninitialized Library
2804 return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
2805 }
2806
NetAdhocctl_CreateEnterGameMode(const char * group_name,int game_type,int num_members,u32 membersAddr,u32 timeout,int flag)2807 int NetAdhocctl_CreateEnterGameMode(const char* group_name, int game_type, int num_members, u32 membersAddr, u32 timeout, int flag) {
2808 if (!netAdhocctlInited)
2809 return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
2810
2811 if (!Memory::IsValidAddress(membersAddr))
2812 return ERROR_NET_ADHOCCTL_INVALID_ARG;
2813
2814 if (game_type < ADHOCCTL_GAMETYPE_1A || game_type > ADHOCCTL_GAMETYPE_2A || num_members < 2 || num_members > 16 || (game_type == ADHOCCTL_GAMETYPE_1A && num_members > 4))
2815 return ERROR_NET_ADHOCCTL_INVALID_ARG;
2816
2817 deleteAllGMB();
2818 gameModePeerPorts.clear();
2819
2820 SceNetEtherAddr* addrs = PSPPointer<SceNetEtherAddr>::Create(membersAddr); // List of participating MAC addresses (started from host)
2821 for (int i = 0; i < num_members; i++) {
2822 requiredGameModeMacs.push_back(*addrs);
2823 DEBUG_LOG(SCENET, "GameMode macAddress#%d=%s", i, mac2str(addrs).c_str());
2824 addrs++;
2825 }
2826 // Add local MAC (Host) first
2827 SceNetEtherAddr localMac;
2828 getLocalMac(&localMac);
2829 gameModeMacs.push_back(localMac);
2830
2831 // FIXME: There seems to be an internal Adhocctl Handler on official prx (running on "SceNetAdhocctl" thread) that will try to sync GameMode timings, by using blocking PTP socket:
2832 // 1). PtpListen (srcMacAddress=0x09F20CB4, srcPort=0x8001, bufSize=0x2000, retryDelay=0x30D40, retryCount=0x33, queue=0x1, unk1=0x0)
2833 // 2). PtpAccpet (peerMacAddr=0x09FE2020, peerPortAddr=0x09FE2010, timeout=0x765BB0, nonblock=0x0) - probably for each clients
2834 // 3). PtpSend (data=0x09F20E18, dataSizeAddr=0x09FE2094, timeout=0x627EDA, nonblock=0x0) - not sure what kind of data nor the size (more than 6 bytes)
2835 // 4). PtpFlush (timeout=0x2DC6C0, nonblock=0x0)
2836 // 5 & 6). PtpClose (accepted socket & listen socket)
2837 // When timeout reached, notify user-defined Adhocctl Handlers with ERROR event (ERROR_NET_ADHOC_TIMEOUT) instead of GAMEMODE event
2838
2839 // We have to wait for all the MACs to have joined to go into CONNECTED state
2840 adhocctlCurrentMode = ADHOCCTL_MODE_GAMEMODE;
2841 adhocConnectionType = ADHOC_CREATE;
2842 netAdhocGameModeEntered = true;
2843 netAdhocEnterGameModeTimeout = timeout;
2844 return NetAdhocctl_Create(group_name);
2845 }
2846
2847 /**
2848 * Connect to the Adhoc control game mode (as a host)
2849 *
2850 * @param group_name - The name of the connection (maximum 8 alphanumeric characters).
2851 * @param game_type - Pass 1.
2852 * @param num_members - The total number of players (including the host).
2853 * @param membersmacAddr - A pointer to a list of the participating mac addresses, host first, then clients.
2854 * @param timeout - Timeout in microseconds.
2855 * @param flag - pass 0.
2856 *
2857 * @return 0 on success, < 0 on error.
2858 */
sceNetAdhocctlCreateEnterGameMode(const char * group_name,int game_type,int num_members,u32 membersAddr,int timeout,int flag)2859 static int sceNetAdhocctlCreateEnterGameMode(const char * group_name, int game_type, int num_members, u32 membersAddr, int timeout, int flag) {
2860 char grpName[ADHOCCTL_GROUPNAME_LEN + 1] = { 0 };
2861 if (group_name)
2862 memcpy(grpName, group_name, ADHOCCTL_GROUPNAME_LEN); // For logging purpose, must not be truncated
2863 WARN_LOG_REPORT_ONCE(sceNetAdhocctlCreateEnterGameMode, SCENET, "UNTESTED sceNetAdhocctlCreateEnterGameMode(%s, %i, %i, %08x, %i, %i) at %08x", grpName, game_type, num_members, membersAddr, timeout, flag, currentMIPS->pc);
2864
2865 return hleLogDebug(SCENET, NetAdhocctl_CreateEnterGameMode(group_name, game_type, num_members, membersAddr, timeout, flag), "");
2866 }
2867
2868 /**
2869 * Connect to the Adhoc control game mode (as a client)
2870 *
2871 * @param group_name - The name of the connection (maximum 8 alphanumeric characters).
2872 * @param hostmacAddr - The mac address of the host.
2873 * @param timeout - Timeout in microseconds.
2874 * @param flag - pass 0.
2875 *
2876 * @return 0 on success, < 0 on error.
2877 */
sceNetAdhocctlJoinEnterGameMode(const char * group_name,const char * hostMac,int timeout,int flag)2878 static int sceNetAdhocctlJoinEnterGameMode(const char * group_name, const char *hostMac, int timeout, int flag) {
2879 char grpName[ADHOCCTL_GROUPNAME_LEN + 1] = { 0 };
2880 if (group_name)
2881 memcpy(grpName, group_name, ADHOCCTL_GROUPNAME_LEN); // For logging purpose, must not be truncated
2882 WARN_LOG_REPORT_ONCE(sceNetAdhocctlJoinEnterGameMode, SCENET, "UNTESTED sceNetAdhocctlJoinEnterGameMode(%s, %s, %i, %i) at %08x", grpName, mac2str((SceNetEtherAddr*)hostMac).c_str(), timeout, flag, currentMIPS->pc);
2883
2884 if (!netAdhocctlInited)
2885 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized");
2886
2887 if (!hostMac)
2888 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg");
2889
2890 deleteAllGMB();
2891
2892 // Add host mac first
2893 gameModeMacs.push_back(*(SceNetEtherAddr*)hostMac);
2894
2895 // FIXME: There seems to be an internal Adhocctl Handler on official prx (running on "SceNetAdhocctl" thread) that will try to sync GameMode timings, by using blocking PTP socket:
2896 // 1). PtpOpen (srcMacAddress=0x09FE2080, srcPort=0x8001, destMacAddress=0x09F20CB4, destPort=0x8001, bufSize=0x2000, retryDelay=0x30D40, retryCount=0x33, unk1=0x0)
2897 // 2). PtpConnect (timeout=0x874CAC, nonblock=0x0) - to host/creator
2898 // 3). PtpRecv (data=0x09F20E18, dataSizeAddr=0x09FE2044, timeout=0x647553, nonblock=0x0) - repeated until data fully received with data address/offset adjusted (increased) and timeout adjusted (decreased), probably also adjusted data size (decreased) on each call
2899 // 4). PtpClose
2900 // When timeout reached, notify user-defined Adhocctl Handlers with ERROR event (ERROR_NET_ADHOC_TIMEOUT) instead of GAMEMODE event
2901
2902 adhocctlCurrentMode = ADHOCCTL_MODE_GAMEMODE;
2903 adhocConnectionType = ADHOC_JOIN;
2904 netAdhocGameModeEntered = true;
2905 netAdhocEnterGameModeTimeout = timeout;
2906 return hleLogDebug(SCENET, NetAdhocctl_Create(group_name), "");
2907 }
2908
2909 /**
2910 * Create and Join a GameMode Network as Host (with Minimum Peer Check)
2911 * @param group_name Virtual Network Name
2912 * @param game_type Network Type (1A, 1B, 2A)
2913 * @param min_members Minimum Number of Peers
2914 * @param num_members Total Number of Peers (including Host)
2915 * @param members MAC Address List of Peers (own MAC at Index 0)
2916 * @param timeout Timeout Value (in Microseconds)
2917 * @param flag Unused Bitflags
2918 * @return 0 on success or... ADHOCCTL_NOT_INITIALIZED, ADHOCCTL_INVALID_ARG, ADHOCCTL_BUSY, ADHOCCTL_CHANNEL_NOT_AVAILABLE
2919 */
sceNetAdhocctlCreateEnterGameModeMin(const char * group_name,int game_type,int min_members,int num_members,u32 membersAddr,u32 timeout,int flag)2920 int sceNetAdhocctlCreateEnterGameModeMin(const char *group_name, int game_type, int min_members, int num_members, u32 membersAddr, u32 timeout, int flag) {
2921 char grpName[ADHOCCTL_GROUPNAME_LEN + 1] = { 0 };
2922 if (group_name)
2923 memcpy(grpName, group_name, ADHOCCTL_GROUPNAME_LEN); // For logging purpose, must not be truncated
2924 WARN_LOG_REPORT_ONCE(sceNetAdhocctlCreateEnterGameModeMin, SCENET, "UNTESTED sceNetAdhocctlCreateEnterGameModeMin(%s, %i, %i, %i, %08x, %d, %i) at %08x", grpName, game_type, min_members, num_members, membersAddr, timeout, flag, currentMIPS->pc);
2925 // We don't really need the Minimum User Check
2926 return hleLogDebug(SCENET, NetAdhocctl_CreateEnterGameMode(group_name, game_type, num_members, membersAddr, timeout, flag), "");
2927 }
2928
NetAdhoc_Term()2929 int NetAdhoc_Term() {
2930 // Since Adhocctl & AdhocMatching uses Sockets & Threads we should terminate them also to release their resources
2931 if (netAdhocMatchingInited)
2932 NetAdhocMatching_Term();
2933 if (netAdhocctlInited)
2934 NetAdhocctl_Term();
2935
2936 // Library is initialized
2937 if (netAdhocInited) {
2938 // Delete GameMode Buffers
2939 deleteAllGMB();
2940
2941 // Delete Adhoc Sockets
2942 deleteAllAdhocSockets();
2943
2944 // Terminate Internet Library
2945 //sceNetInetTerm();
2946
2947 // Unload Internet Modules (Just keep it in memory... unloading crashes?!)
2948 // if (_manage_modules != 0) sceUtilityUnloadModule(PSP_MODULE_NET_INET);
2949 // Library shutdown
2950
2951 netAdhocInited = false;
2952 //return hleLogSuccessInfoI(SCENET, 0);
2953 }
2954 /*else {
2955 // TODO: Reportedly returns SCE_KERNEL_ERROR_LWMUTEX_NOT_FOUND in some cases?
2956 // Only seen returning 0 in tests.
2957 return hleLogWarning(SCENET, 0, "already uninitialized");
2958 }*/
2959
2960 return 0;
2961 }
2962
sceNetAdhocTerm()2963 int sceNetAdhocTerm() {
2964 // WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup all the sockets right?
2965 int retval = NetAdhoc_Term();
2966
2967 hleEatMicro(adhocDefaultDelay);
2968 return hleLogSuccessInfoI(SCENET, retval);
2969 }
2970
sceNetAdhocGetPdpStat(u32 structSize,u32 structAddr)2971 static int sceNetAdhocGetPdpStat(u32 structSize, u32 structAddr) {
2972 VERBOSE_LOG(SCENET, "sceNetAdhocGetPdpStat(%08x, %08x) at %08x", structSize, structAddr, currentMIPS->pc);
2973
2974 // Library is initialized
2975 if (netAdhocInited)
2976 {
2977 s32_le *buflen = NULL;
2978 if (Memory::IsValidAddress(structSize)) buflen = (s32_le *)Memory::GetPointer(structSize);
2979 SceNetAdhocPdpStat *buf = NULL;
2980 if (Memory::IsValidAddress(structAddr)) buf = (SceNetAdhocPdpStat *)Memory::GetPointer(structAddr);
2981
2982 // Socket Count
2983 int socketcount = getPDPSocketCount();
2984
2985 // Length Returner Mode
2986 if (buflen != NULL && buf == NULL)
2987 {
2988 // Return Required Size
2989 *buflen = sizeof(SceNetAdhocPdpStat) * socketcount;
2990 VERBOSE_LOG(SCENET, "Stat PDP Socket Count: %d", socketcount);
2991
2992 // Success
2993 return 0;
2994 }
2995
2996 // Status Returner Mode
2997 else if (buflen != NULL && buf != NULL)
2998 {
2999 // Figure out how many Sockets we will return
3000 int count = *buflen / sizeof(SceNetAdhocPdpStat);
3001 if (count > socketcount) count = socketcount;
3002
3003 // Copy Counter
3004 int i = 0;
3005
3006 // Iterate Translation Table
3007 for (int j = 0; j < MAX_SOCKET && i < count; j++)
3008 {
3009 // Valid Socket Entry
3010 auto sock = adhocSockets[j];
3011 if (sock != NULL && sock->type == SOCK_PDP) {
3012 // Set available bytes to be received. With FIONREAD There might be ghosting 1 byte in recv buffer when remote peer's socket got closed (ie. Warriors Orochi 2) Attempting to recv this ghost 1 byte will result to socket error 10054 (may need to disable SIO_UDP_CONNRESET error)
3013 // It seems real PSP respecting the socket buffer size arg, so we may need to cap the value up to the buffer size arg since we use larger buffer, for PDP/UDP the total size must not contains partial/truncated message to avoid data loss.
3014 // TODO: We may need to manage PDP messages ourself by reading each msg 1-by-1 and moving it to our internal buffer(msg array) in order to calculate the correct messages size that can fit into buffer size when there are more than 1 messages in the recv buffer (simulate FIONREAD)
3015 sock->data.pdp.rcv_sb_cc = getAvailToRecv(sock->data.pdp.id, sock->buffer_size);
3016
3017 // Copy Socket Data from Internal Memory
3018 memcpy(&buf[i], &sock->data.pdp, sizeof(SceNetAdhocPdpStat));
3019
3020 // Fix Client View Socket ID
3021 buf[i].id = j + 1;
3022
3023 // Write End of List Reference
3024 buf[i].next = 0;
3025
3026 // Link Previous Element
3027 if (i > 0)
3028 buf[i - 1].next = structAddr + (i * sizeof(SceNetAdhocPdpStat));
3029
3030 VERBOSE_LOG(SCENET, "Stat PDP Socket Id: %d (%d), LPort: %d, RecvSbCC: %d", buf[i].id, sock->data.pdp.id, buf[i].lport, buf[i].rcv_sb_cc);
3031
3032 // Increment Counter
3033 i++;
3034 }
3035 }
3036
3037 // Update Buffer Length
3038 *buflen = i * sizeof(SceNetAdhocPdpStat);
3039
3040 // Success
3041 return 0;
3042 }
3043
3044 // Invalid Arguments
3045 return ERROR_NET_ADHOC_INVALID_ARG;
3046 }
3047
3048 // Library is uninitialized
3049 return ERROR_NET_ADHOC_NOT_INITIALIZED;
3050 }
3051
3052
3053 /**
3054 * Adhoc Emulator PTP Socket List Getter
3055 * @param buflen IN: Length of Buffer in Bytes OUT: Required Length of Buffer in Bytes
3056 * @param buf PTP Socket List Buffer (can be NULL if you wish to receive Required Length)
3057 * @return 0 on success or... ADHOC_INVALID_ARG, ADHOC_NOT_INITIALIZED
3058 */
sceNetAdhocGetPtpStat(u32 structSize,u32 structAddr)3059 static int sceNetAdhocGetPtpStat(u32 structSize, u32 structAddr) {
3060 // Spams a lot
3061 VERBOSE_LOG(SCENET,"sceNetAdhocGetPtpStat(%08x, %08x) at %08x",structSize,structAddr,currentMIPS->pc);
3062
3063 s32_le *buflen = NULL;
3064 if (Memory::IsValidAddress(structSize)) buflen = (s32_le *)Memory::GetPointer(structSize);
3065 SceNetAdhocPtpStat *buf = NULL;
3066 if (Memory::IsValidAddress(structAddr)) buf = (SceNetAdhocPtpStat *)Memory::GetPointer(structAddr);
3067
3068 // Library is initialized
3069 if (netAdhocInited) {
3070 // Socket Count
3071 int socketcount = getPTPSocketCount();
3072
3073 // Length Returner Mode
3074 if (buflen != NULL && buf == NULL) {
3075 // Return Required Size
3076 *buflen = sizeof(SceNetAdhocPtpStat) * socketcount;
3077 VERBOSE_LOG(SCENET, "Stat PTP Socket Count: %d", socketcount);
3078
3079 // Success
3080 return 0;
3081 }
3082
3083 // Status Returner Mode
3084 else if (buflen != NULL && buf != NULL) {
3085 // Figure out how many Sockets we will return
3086 int count = *buflen / sizeof(SceNetAdhocPtpStat);
3087 if (count > socketcount) count = socketcount;
3088
3089 // Copy Counter
3090 int i = 0;
3091
3092 // Iterate Sockets
3093 for (int j = 0; j < MAX_SOCKET && i < count; j++) {
3094 // Valid Socket Entry
3095 auto sock = adhocSockets[j];
3096 if ( sock != NULL && sock->type == SOCK_PTP) {
3097 // Update connection state.
3098 // GvG Next Plus relies on GetPtpStat to determine if Connection has been Established or not, but should not be updated too long for GvG to work, and should not be updated too fast(need to be 1 frame after PollSocket checking for ADHOC_EV_CONNECT) for Bleach Heat the Soul 7 to work properly.
3099 if ((sock->data.ptp.state == ADHOC_PTP_STATE_SYN_SENT || sock->data.ptp.state == ADHOC_PTP_STATE_SYN_RCVD) && (static_cast<s64>(CoreTiming::GetGlobalTimeUsScaled() - sock->lastAttempt) > 35000/*sock->retry_interval*/)) {
3100 // FIXME: May be we should poll all of them together on a single poll call instead of each socket separately?
3101 if (IsSocketReady(sock->data.ptp.id, true, true) > 0) {
3102 sock->data.ptp.state = ADHOC_PTP_STATE_ESTABLISHED;
3103 }
3104 }
3105
3106 // Set available bytes to be received
3107 sock->data.ptp.rcv_sb_cc = getAvailToRecv(sock->data.ptp.id);
3108 // It seems real PSP respecting the socket buffer size arg, so we may need to cap the value to the buffer size arg since we use larger buffer
3109 sock->data.ptp.rcv_sb_cc = std::min(sock->data.ptp.rcv_sb_cc, (u32_le)sock->buffer_size);
3110
3111 // Copy Socket Data from internal Memory
3112 memcpy(&buf[i], &sock->data.ptp, sizeof(SceNetAdhocPtpStat));
3113
3114 // Fix Client View Socket ID
3115 buf[i].id = j + 1;
3116
3117 // Write End of List Reference
3118 buf[i].next = 0;
3119
3120 // Link previous Element to this one
3121 if (i > 0)
3122 buf[i - 1].next = structAddr + (i * sizeof(SceNetAdhocPtpStat));
3123
3124 VERBOSE_LOG(SCENET, "Stat PTP Socket Id: %d (%d), LPort: %d, RecvSbCC: %d, State: %d", buf[i].id, sock->data.ptp.id, buf[i].lport, buf[i].rcv_sb_cc, buf[i].state);
3125
3126 // Increment Counter
3127 i++;
3128 }
3129 }
3130
3131 // Update Buffer Length
3132 *buflen = i * sizeof(SceNetAdhocPtpStat);
3133
3134 hleEatMicro(50); // Not sure how long it takes, since GetPtpStat didn't get logged when using prx files on JPCSP
3135 // Success
3136 return 0;
3137 }
3138
3139 // Invalid Arguments
3140 return ERROR_NET_ADHOC_INVALID_ARG;
3141 }
3142
3143 // Library is uninitialized
3144 return ERROR_NET_ADHOC_NOT_INITIALIZED;
3145 }
3146
3147
3148 /**
3149 * Adhoc Emulator PTP Active Socket Creator
3150 * @param saddr Local MAC (Unused)
3151 * @param sport Local Binding Port
3152 * @param daddr Target MAC
3153 * @param dport Target Port
3154 * @param bufsize Socket Buffer Size
3155 * @param rexmt_int Retransmit Interval (in Microseconds)
3156 * @param rexmt_cnt Retransmit Count
3157 * @param flag Bitflags (Unused)
3158 * @return Socket ID > 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_ADDR, ADHOC_INVALID_PORT
3159 */
sceNetAdhocPtpOpen(const char * srcmac,int sport,const char * dstmac,int dport,int bufsize,int rexmt_int,int rexmt_cnt,int flag)3160 static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, int dport, int bufsize, int rexmt_int, int rexmt_cnt, int flag) {
3161 INFO_LOG(SCENET, "sceNetAdhocPtpOpen(%s, %d, %s, %d, %d, %d, %d, %d) at %08x", mac2str((SceNetEtherAddr*)srcmac).c_str(), sport, mac2str((SceNetEtherAddr*)dstmac).c_str(),dport,bufsize, rexmt_int, rexmt_cnt, flag, currentMIPS->pc);
3162 if (!g_Config.bEnableWlan) {
3163 return -1;
3164 }
3165 SceNetEtherAddr* saddr = (SceNetEtherAddr*)srcmac;
3166 SceNetEtherAddr* daddr = (SceNetEtherAddr*)dstmac;
3167 bool isClient = false;
3168 // Library is initialized
3169 if (netAdhocInited) {
3170 // Some games (ie. DBZ Shin Budokai 2) might be getting the saddr/srcmac content from SaveState and causing problems if current MAC is different :( So we try to fix it here
3171 if (saddr != NULL) {
3172 getLocalMac(saddr);
3173 }
3174 // Valid Addresses. FIXME: MAC only valid after successful attempt to Create/Connect/Join a Group? (ie. adhocctlCurrentMode != ADHOCCTL_MODE_NONE)
3175 if ((adhocctlCurrentMode != ADHOCCTL_MODE_NONE) && saddr != NULL && isLocalMAC(saddr) && daddr != NULL && !isBroadcastMAC(daddr) && !isZeroMAC(daddr)) {
3176 // Dissidia 012 will try to reOpen the port without Closing the old one first when PtpConnect failed to try again.
3177 if (isPTPPortInUse(sport, false, daddr, dport)) {
3178 // FIXME: When PORT_IN_USE error occured it seems the index to the socket id also increased, which means it tries to create & bind the socket first and then closes it due to failed to bind
3179 return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_IN_USE, "port in use");
3180 }
3181
3182 // Random Port required
3183 if (sport == 0) {
3184 isClient = true;
3185 //sport 0 should be shifted back to 0 when using offset Phantasy Star Portable 2 use this
3186 sport = -static_cast<int>(portOffset);
3187 }
3188
3189 // Valid Arguments
3190 if (bufsize > 0 && rexmt_int > 0 && rexmt_cnt > 0) {
3191 // Create Infrastructure Socket
3192 int tcpsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
3193
3194 // Valid Socket produced
3195 if (tcpsocket > 0) {
3196 // Change socket MSS
3197 setSockMSS(tcpsocket, PSP_ADHOC_PTP_MSS);
3198
3199 // Change socket buffer size to be consistent on all platforms.
3200 setSockBufferSize(tcpsocket, SO_SNDBUF, bufsize*5); //PSP_ADHOC_PTP_MSS
3201 setSockBufferSize(tcpsocket, SO_RCVBUF, bufsize*10); //PSP_ADHOC_PTP_MSS*10
3202
3203 // Enable KeepAlive
3204 setSockKeepAlive(tcpsocket, true, rexmt_int / 1000000L, rexmt_cnt);
3205
3206 // Ignore SIGPIPE when supported (ie. BSD/MacOS)
3207 setSockNoSIGPIPE(tcpsocket, 1);
3208
3209 // Enable Port Re-use
3210 setSockReuseAddrPort(tcpsocket);
3211
3212 // Apply Default Send Timeout Settings to Socket
3213 setSockTimeout(tcpsocket, SO_SNDTIMEO, rexmt_int);
3214
3215 // Disable Nagle Algo to send immediately. Or may be we shouldn't disable Nagle since there is PtpFlush function?
3216 setSockNoDelay(tcpsocket, 1);
3217
3218 // Binding Information for local Port
3219 struct sockaddr_in addr;
3220 // addr.sin_len = sizeof(addr);
3221 addr.sin_family = AF_INET;
3222 addr.sin_addr.s_addr = INADDR_ANY;
3223 if (isLocalServer) {
3224 getLocalIp(&addr);
3225 }
3226 uint16_t requestedport = static_cast<int>(sport + static_cast<int>(portOffset));
3227 // Avoid getting random port due to port offset when original port wasn't 0 (ie. original_port + port_offset = 65536 = 0)
3228 if (requestedport == 0 && sport > 0)
3229 requestedport = 65535; // Hopefully it will be safe to default it to 65535 since there can't be more than one port that can bumped into 65536
3230 // Show a warning about privileged ports
3231 if (requestedport != 0 && requestedport < 1024) {
3232 WARN_LOG(SCENET, "sceNetAdhocPtpOpen - Ports below 1024(ie. %hu) may require Admin Privileges", requestedport);
3233 }
3234 addr.sin_port = htons(requestedport);
3235
3236 // Bound Socket to local Port
3237 if (bind(tcpsocket, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
3238 // Update sport with the port assigned internal->lport = ntohs(local.sin_port)
3239 socklen_t len = sizeof(addr);
3240 if (getsockname(tcpsocket, (struct sockaddr*)&addr, &len) == 0) {
3241 uint16_t boundport = ntohs(addr.sin_port);
3242 if (sport + static_cast<int>(portOffset) >= 65536 || static_cast<int>(boundport) - static_cast<int>(portOffset) <= 0)
3243 WARN_LOG(SCENET, "sceNetAdhocPtpOpen - Wrapped Port Detected: Original(%d) -> Requested(%d), Bound(%d) -> BoundOriginal(%d)", sport, requestedport, boundport, boundport - portOffset);
3244 sport = boundport - portOffset;
3245 }
3246
3247 // Allocate Memory
3248 AdhocSocket* internal = (AdhocSocket*)malloc(sizeof(AdhocSocket));
3249
3250 // Allocated Memory
3251 if (internal != NULL) {
3252 // Find Free Translator ID
3253 // FIXME: We should probably use an increasing index instead of looking for an empty slot from beginning if we want to simulate a real socket id
3254 int i = 0;
3255 for (; i < MAX_SOCKET; i++) if (adhocSockets[i] == NULL) break;
3256
3257 // Found Free Translator ID
3258 if (i < MAX_SOCKET) {
3259 // Clear Memory
3260 memset(internal, 0, sizeof(AdhocSocket));
3261
3262 // Socket Type
3263 internal->type = SOCK_PTP;
3264 internal->retry_interval = rexmt_int;
3265 internal->retry_count = rexmt_cnt;
3266 internal->nonblocking = flag;
3267 internal->buffer_size = bufsize;
3268
3269 // Copy Infrastructure Socket ID
3270 internal->data.ptp.id = tcpsocket;
3271
3272 // Copy Address Information
3273 internal->data.ptp.laddr = *saddr;
3274 internal->data.ptp.paddr = *daddr;
3275 internal->data.ptp.lport = sport;
3276 internal->data.ptp.pport = dport;
3277
3278 // Link PTP Socket
3279 adhocSockets[i] = internal;
3280
3281 // Add Port Forward to Router. We may not even need to forward this local port, since PtpOpen usually have port 0 (any port) as source port and followed by PtpConnect (which mean acting as Client), right?
3282 //sceNetPortOpen("TCP", sport);
3283 if (!isClient)
3284 UPnP_Add(IP_PROTOCOL_TCP, isOriPort ? sport : sport + portOffset, sport + portOffset);
3285
3286 // Switch to non-blocking for futher usage
3287 changeBlockingMode(tcpsocket, 1);
3288
3289 // Initiate PtpConnect (ie. The Warrior seems to try to PtpSend right after PtpOpen without trying to PtpConnect first)
3290 NetAdhocPtp_Connect(i + 1, rexmt_int, 1, false);
3291
3292 // Workaround to give some time to get connected before returning from PtpOpen over high latency
3293 if (g_Config.bForcedFirstConnect && internal->attemptCount == 1)
3294 hleDelayResult(i + 1, "delayed ptpopen", rexmt_int);
3295
3296 // Return PTP Socket Pointer
3297 return hleLogDebug(SCENET, i + 1, "success");
3298 }
3299
3300 // Free Memory
3301 free(internal);
3302 }
3303 }
3304 else {
3305 ERROR_LOG(SCENET, "Socket error (%i) when binding port %u", errno, ntohs(addr.sin_port));
3306 auto n = GetI18NCategory("Networking");
3307 host->NotifyUserMessage(std::string(n->T("Failed to Bind Port")) + " " + std::to_string(sport + portOffset) + "\n" + std::string(n->T("Please change your Port Offset")), 3.0, 0x0000ff);
3308 }
3309
3310 // Close Socket
3311 closesocket(tcpsocket);
3312
3313 // Port not available (exclusively in use?)
3314 return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_NOT_AVAIL, "port not available"); // ERROR_NET_ADHOC_PORT_IN_USE; // ERROR_NET_ADHOC_INVALID_PORT;
3315 }
3316 }
3317
3318 // Invalid Arguments
3319 return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg");
3320 }
3321
3322 // Invalid Addresses
3323 return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ADDR, "invalid address"); // ERROR_NET_ADHOC_INVALID_ARG;
3324 }
3325
3326 // Library is uninitialized
3327 return hleLogDebug(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "adhoc not initialized");
3328 }
3329
3330 // On a POSIX accept, returned socket may inherits properties from the listening socket, does PtpAccept also have similar behavior?
AcceptPtpSocket(int ptpId,int newsocket,sockaddr_in & peeraddr,SceNetEtherAddr * addr,u16_le * port)3331 int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEtherAddr* addr, u16_le* port) {
3332 // Cast Socket
3333 auto socket = adhocSockets[ptpId - 1];
3334 auto& ptpsocket = socket->data.ptp;
3335
3336 // Ignore SIGPIPE when supported (ie. BSD/MacOS)
3337 setSockNoSIGPIPE(newsocket, 1);
3338
3339 // Enable Port Re-use
3340 setSockReuseAddrPort(newsocket);
3341
3342 // Disable Nagle Algo to send immediately. Or may be we shouldn't disable Nagle since there is PtpFlush function?
3343 setSockNoDelay(newsocket, 1);
3344
3345 // Local Address Information
3346 struct sockaddr_in local;
3347 memset(&local, 0, sizeof(local));
3348 socklen_t locallen = sizeof(local);
3349
3350 // Grab Local Address
3351 if (getsockname(newsocket, (struct sockaddr*)&local, &locallen) == 0) {
3352 // Peer MAC
3353 SceNetEtherAddr mac;
3354
3355 // Find Peer MAC
3356 if (resolveIP(peeraddr.sin_addr.s_addr, &mac)) {
3357 // Allocate Memory
3358 AdhocSocket* internal = (AdhocSocket*)malloc(sizeof(AdhocSocket));
3359
3360 // Allocated Memory
3361 if (internal != NULL) {
3362 // Find Free Translator ID
3363 // FIXME: We should probably use an increasing index instead of looking for an empty slot from beginning if we want to simulate a real socket id
3364 int i = 0;
3365 for (; i < MAX_SOCKET; i++) if (adhocSockets[i] == NULL) break;
3366
3367 // Found Free Translator ID
3368 if (i < MAX_SOCKET) {
3369 // Clear Memory
3370 memset(internal, 0, sizeof(AdhocSocket));
3371
3372 // Inherits some of Listening socket's properties
3373 // Socket Type
3374 internal->type = SOCK_PTP;
3375 internal->nonblocking = socket->nonblocking;
3376 internal->attemptCount = 1; // Used to differentiate between closed state of disconnected socket and not connected yet.
3377 internal->retry_interval = socket->retry_interval;
3378 internal->retry_count = socket->retry_count;
3379 // Enable KeepAlive
3380 setSockKeepAlive(newsocket, true, internal->retry_interval / 1000000L, internal->retry_count);
3381
3382 // Copy Socket Descriptor to Structure
3383 internal->data.ptp.id = newsocket;
3384
3385 // Change socket MSS
3386 setSockMSS(newsocket, PSP_ADHOC_PTP_MSS);
3387
3388 // Set Default Buffer Size or inherit the size?
3389 internal->buffer_size = socket->buffer_size;
3390 setSockBufferSize(newsocket, SO_SNDBUF, internal->buffer_size*5); //PSP_ADHOC_PTP_MSS
3391 setSockBufferSize(newsocket, SO_RCVBUF, internal->buffer_size*10); //PSP_ADHOC_PTP_MSS*10
3392
3393 // Copy Local Address Data to Structure
3394 getLocalMac(&internal->data.ptp.laddr);
3395 internal->data.ptp.lport = ntohs(local.sin_port) - portOffset;
3396
3397 // Copy Peer Address Data to Structure
3398 internal->data.ptp.paddr = mac;
3399 internal->data.ptp.pport = ntohs(peeraddr.sin_port) - portOffset;
3400
3401 // Set Connection State
3402 internal->data.ptp.state = ADHOC_PTP_STATE_ESTABLISHED;
3403
3404 // Return Peer Address Information
3405 *addr = internal->data.ptp.paddr;
3406 if (port != NULL) *port = internal->data.ptp.pport;
3407
3408 // Link PTP Socket
3409 adhocSockets[i] = internal;
3410
3411 // Add Port Forward to Router. Or may be doesn't need to be forwarded since local port already accessible from outside if others were able to connect & get accepted at this point, right?
3412 //sceNetPortOpen("TCP", internal->lport);
3413 //g_PortManager.Add(IP_PROTOCOL_TCP, internal->lport + portOffset);
3414
3415 // Switch to non-blocking for futher usage
3416 changeBlockingMode(newsocket, 1);
3417
3418 INFO_LOG(SCENET, "sceNetAdhocPtpAccept[%i->%i:%u]: Established (%s:%u) - state: %d", ptpId, i + 1, internal->data.ptp.lport, ip2str(peeraddr.sin_addr).c_str(), internal->data.ptp.pport, internal->data.ptp.state);
3419
3420 // Return Socket
3421 return i + 1;
3422 }
3423
3424 // Free Memory
3425 free(internal);
3426 }
3427 }
3428 }
3429
3430 // Close Socket
3431 closesocket(newsocket);
3432
3433 ERROR_LOG(SCENET, "sceNetAdhocPtpAccept[%i]: Failed (Socket Closed)", ptpId);
3434 return -1;
3435 }
3436
3437 /**
3438 * Adhoc Emulator PTP Connection Acceptor
3439 * @param id Socket File Descriptor
3440 * @param addr OUT: Peer MAC Address
3441 * @param port OUT: Peer Port
3442 * @param timeout Accept Timeout (in Microseconds)
3443 * @param flag Nonblocking Flag
3444 * @return Socket ID >= 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_SOCKET_ALERTED, ADHOC_SOCKET_ID_NOT_AVAIL, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_NOT_LISTENED, ADHOC_THREAD_ABORTED, NET_INTERNAL
3445 */
sceNetAdhocPtpAccept(int id,u32 peerMacAddrPtr,u32 peerPortPtr,int timeout,int flag)3446 static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int timeout, int flag) {
3447
3448 SceNetEtherAddr * addr = NULL;
3449 if (Memory::IsValidAddress(peerMacAddrPtr)) {
3450 addr = PSPPointer<SceNetEtherAddr>::Create(peerMacAddrPtr);
3451 }
3452 uint16_t * port = NULL; //
3453 if (Memory::IsValidAddress(peerPortPtr)) {
3454 port = (uint16_t *)Memory::GetPointer(peerPortPtr);
3455 }
3456 if (flag == 0) { // Prevent spamming Debug Log with retries of non-bocking socket
3457 DEBUG_LOG(SCENET, "sceNetAdhocPtpAccept(%d, [%08x]=%s, [%08x]=%u, %d, %u) at %08x", id, peerMacAddrPtr, mac2str(addr).c_str(), peerPortPtr, port ? *port : -1, timeout, flag, currentMIPS->pc);
3458 } else {
3459 VERBOSE_LOG(SCENET, "sceNetAdhocPtpAccept(%d, [%08x]=%s, [%08x]=%u, %d, %u) at %08x", id, peerMacAddrPtr, mac2str(addr).c_str(), peerPortPtr, port ? *port : -1, timeout, flag, currentMIPS->pc);
3460 }
3461 if (!g_Config.bEnableWlan) {
3462 return -1;
3463 }
3464
3465 // Library is initialized
3466 if (netAdhocInited) {
3467 // Valid Arguments
3468 if (addr != NULL) { //GTA:VCS seems to use 0 for the portPtr
3469 // Valid Socket
3470 if (id > 0 && id <= MAX_SOCKET && adhocSockets[id - 1] != NULL) {
3471 // Cast Socket
3472 auto socket = adhocSockets[id - 1];
3473 auto& ptpsocket = socket->data.ptp;
3474 socket->nonblocking = flag;
3475
3476 if (socket->flags & ADHOC_F_ALERTACCEPT) {
3477 socket->alerted_flags |= ADHOC_F_ALERTACCEPT;
3478
3479 return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted");
3480 }
3481
3482 // Listener Socket
3483 if (ptpsocket.state == ADHOC_PTP_STATE_LISTEN) {
3484 hleEatMicro(50);
3485 // Address Information
3486 struct sockaddr_in peeraddr;
3487 memset(&peeraddr, 0, sizeof(peeraddr));
3488 socklen_t peeraddrlen = sizeof(peeraddr);
3489 int error;
3490
3491 // Check if listening socket is ready to accept
3492 int newsocket = IsSocketReady(ptpsocket.id, true, false, &error);
3493 if (newsocket > 0) {
3494 // Accept Connection
3495 newsocket = accept(ptpsocket.id, (struct sockaddr*)&peeraddr, &peeraddrlen);
3496 error = errno;
3497 }
3498
3499 if (newsocket == 0 || (newsocket == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK))) {
3500 if (flag == 0) {
3501 // Simulate blocking behaviour with non-blocking socket
3502 u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id;
3503 return WaitBlockingAdhocSocket(threadSocketId, PTP_ACCEPT, id, nullptr, nullptr, timeout, addr, port, "ptp accept");
3504 }
3505 // Prevent spamming Debug Log with retries of non-bocking socket
3506 else {
3507 VERBOSE_LOG(SCENET, "sceNetAdhocPtpAccept[%i]: Socket Error (%i)", id, error);
3508 }
3509 }
3510
3511 // Accepted New Connection
3512 if (newsocket > 0) {
3513 int newid = AcceptPtpSocket(id, newsocket, peeraddr, addr, port);
3514 if (newid >= 0)
3515 return newid;
3516 }
3517
3518 // Action would block
3519 if (flag)
3520 return hleLogSuccessVerboseX(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block");
3521
3522 // Timeout
3523 return hleLogSuccessVerboseX(SCENET, ERROR_NET_ADHOC_TIMEOUT, "timeout");
3524 }
3525
3526 // Client Socket
3527 return hleLogSuccessVerboseX(SCENET, ERROR_NET_ADHOC_NOT_LISTENED, "not listened");
3528 }
3529
3530 // Invalid Socket
3531 return hleLogSuccessVerboseX(SCENET, ERROR_NET_ADHOC_INVALID_SOCKET_ID, "invalid socket id");
3532 }
3533
3534 // Invalid Arguments
3535 return hleLogSuccessVerboseX(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg");
3536 }
3537
3538 // Library is uninitialized
3539 return hleLogSuccessVerboseX(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "not initialized");
3540 }
3541
NetAdhocPtp_Connect(int id,int timeout,int flag,bool allowForcedConnect)3542 int NetAdhocPtp_Connect(int id, int timeout, int flag, bool allowForcedConnect) {
3543 // Library is initialized
3544 if (netAdhocInited)
3545 {
3546 // Valid Socket
3547 if (id > 0 && id <= MAX_SOCKET && adhocSockets[id - 1] != NULL) {
3548 // Cast Socket
3549 auto socket = adhocSockets[id - 1];
3550 auto& ptpsocket = socket->data.ptp;
3551 socket->nonblocking = flag;
3552
3553 if (socket->flags & ADHOC_F_ALERTCONNECT) {
3554 socket->alerted_flags |= ADHOC_F_ALERTCONNECT;
3555
3556 return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted");
3557 }
3558
3559 // Phantasy Star Portable 2 will try to reconnect even when previous connect already success, so we should return success too if it's already connected
3560 if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED)
3561 return 0;
3562
3563 // Valid Client Socket
3564 if (ptpsocket.state == ADHOC_PTP_STATE_CLOSED || ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT) {
3565 hleEatMicro(50);
3566 // Target Address
3567 struct sockaddr_in sin;
3568 memset(&sin, 0, sizeof(sin));
3569
3570 // Setup Target Address
3571 // sin.sin_len = sizeof(sin);
3572 sin.sin_family = AF_INET;
3573 sin.sin_port = htons(ptpsocket.pport + portOffset);
3574
3575 // Grab Peer IP
3576 if (resolveMAC(&ptpsocket.paddr, (uint32_t*)&sin.sin_addr.s_addr)) {
3577 // Some games (ie. PSP2) might try to talk to it's self, not sure if they talked through WAN or LAN when using public Adhoc Server tho
3578 sin.sin_port = htons(ptpsocket.pport + ((isOriPort && !isPrivateIP(sin.sin_addr.s_addr)) ? 0 : portOffset));
3579
3580 // Connect Socket to Peer
3581 // NOTE: Based on what i read at stackoverflow, The First Non-blocking POSIX connect will always returns EAGAIN/EWOULDBLOCK because it returns without waiting for ACK/handshake, But GvG Next Plus is treating non-blocking PtpConnect just like blocking connect, May be on a real PSP the first non-blocking sceNetAdhocPtpConnect can be successfull?
3582 int connectresult = connect(ptpsocket.id, (struct sockaddr*)&sin, sizeof(sin));
3583
3584 // Grab Error Code
3585 int errorcode = errno;
3586
3587 if (connectresult == SOCKET_ERROR) {
3588 if (errorcode == EAGAIN || errorcode == EWOULDBLOCK || errorcode == EALREADY || errorcode == EISCONN)
3589 DEBUG_LOG(SCENET, "sceNetAdhocPtpConnect[%i]: Socket Error (%i) to %s:%u", id, errorcode, ip2str(sin.sin_addr).c_str(), ptpsocket.pport);
3590 else
3591 ERROR_LOG(SCENET, "sceNetAdhocPtpConnect[%i]: Socket Error (%i) to %s:%u", id, errorcode, ip2str(sin.sin_addr).c_str(), ptpsocket.pport);
3592 }
3593
3594 // Instant Connection (Lucky!)
3595 if (connectresult != SOCKET_ERROR || errorcode == EISCONN) {
3596 socket->attemptCount++;
3597 socket->lastAttempt = CoreTiming::GetGlobalTimeUsScaled();
3598 // Set Connected State
3599 ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED;
3600
3601 INFO_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Already Connected to %s:%u", id, ptpsocket.lport, ip2str(sin.sin_addr).c_str(), ptpsocket.pport);
3602 // Success
3603 return 0;
3604 }
3605
3606 // Error handling
3607 else if (connectresult == SOCKET_ERROR) {
3608 // Connection in Progress
3609 if (connectInProgress(errorcode)) {
3610 socket->data.ptp.state = ADHOC_PTP_STATE_SYN_SENT;
3611 socket->attemptCount++;
3612 socket->lastAttempt = CoreTiming::GetGlobalTimeUsScaled();
3613 // Blocking Mode
3614 // Workaround: Forcing first attempt to be blocking to prevent issue related to lobby or high latency networks. (can be useful for GvG Next Plus, Dissidia 012, and Fate Unlimited Codes)
3615 if (!flag || (allowForcedConnect && g_Config.bForcedFirstConnect && socket->attemptCount == 1)) {
3616 // Simulate blocking behaviour with non-blocking socket
3617 u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id;
3618 return WaitBlockingAdhocSocket(threadSocketId, PTP_CONNECT, id, nullptr, nullptr, (flag) ? std::max((int)socket->retry_interval, timeout) : timeout, nullptr, nullptr, "ptp connect");
3619 }
3620 // NonBlocking Mode
3621 else {
3622 return hleLogDebug(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block");
3623 }
3624 }
3625 // No connection could be made because the target device actively refused it (on Windows/Linux/Android), or no one listening on the remote address (on Linux/Android).
3626 else if (errorcode == ECONNREFUSED) {
3627 // Workaround for ERROR_NET_ADHOC_CONNECTION_REFUSED to be more cross-platform, since there is no way to simulate ERROR_NET_ADHOC_CONNECTION_REFUSED properly on Windows
3628 if (flag)
3629 return hleLogError(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "connection refused workaround");
3630 else
3631 return hleLogError(SCENET, ERROR_NET_ADHOC_TIMEOUT, "connection refused workaround");
3632 }
3633 }
3634 }
3635
3636 // Peer not found
3637 return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ADDR, "invalid address"); // ERROR_NET_ADHOC_WOULD_BLOCK / ERROR_NET_ADHOC_TIMEOUT
3638 }
3639
3640 // Not a valid Client Socket
3641 return hleLogDebug(SCENET, ERROR_NET_ADHOC_NOT_OPENED, "not opened");
3642 }
3643
3644 // Invalid Socket
3645 return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_SOCKET_ID, "invalid socket id");
3646 }
3647
3648 // Library is uninitialized
3649 return hleLogDebug(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "not initialized");
3650 }
3651
3652 /**
3653 * Adhoc Emulator PTP Connection Opener
3654 * @param id Socket File Descriptor
3655 * @param timeout Connect Timeout (in Microseconds)
3656 * @param flag Nonblocking Flag
3657 * @return 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_CONNECTION_REFUSED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_NOT_OPENED, ADHOC_THREAD_ABORTED, NET_INTERNAL
3658 */
sceNetAdhocPtpConnect(int id,int timeout,int flag)3659 static int sceNetAdhocPtpConnect(int id, int timeout, int flag) {
3660 INFO_LOG(SCENET, "sceNetAdhocPtpConnect(%i, %i, %i) at %08x", id, timeout, flag, currentMIPS->pc);
3661 if (!g_Config.bEnableWlan) {
3662 return -1;
3663 }
3664
3665 return NetAdhocPtp_Connect(id, timeout, flag);
3666 }
3667
NetAdhocPtp_Close(int id,int unknown)3668 int NetAdhocPtp_Close(int id, int unknown) {
3669 // Library is initialized
3670 if (netAdhocInited) {
3671 // Valid Arguments
3672 if (id > 0 && id <= MAX_SOCKET) {
3673 // Cast Socket
3674 auto socket = adhocSockets[id - 1];
3675
3676 // Valid Socket
3677 if (socket != NULL && socket->type == SOCK_PTP) {
3678 // Close Connection
3679 struct linger sl;
3680 sl.l_onoff = 1; // non-zero value enables linger option in kernel
3681 sl.l_linger = 0; // timeout interval in seconds
3682 setsockopt(socket->data.ptp.id, SOL_SOCKET, SO_LINGER, (const char*)&sl, sizeof(sl));
3683 shutdown(socket->data.ptp.id, SD_BOTH);
3684 closesocket(socket->data.ptp.id);
3685
3686 // Remove Port Forward from Router
3687 //sceNetPortClose("TCP", socket->lport);
3688 //g_PortManager.Remove(IP_PROTOCOL_TCP, isOriPort ? socket->lport : socket->lport + portOffset); // Let's not remove mapping in real-time as it could cause lags/disconnection when joining a room with slow routers
3689
3690 // Free Memory
3691 free(socket);
3692
3693 // Free Reference
3694 adhocSockets[id - 1] = NULL;
3695
3696 // Success
3697 return 0;
3698 }
3699
3700 return ERROR_NET_ADHOC_INVALID_SOCKET_ID;
3701 }
3702
3703 // Invalid Argument
3704 return ERROR_NET_ADHOC_INVALID_ARG;
3705 }
3706
3707 // Library is uninitialized
3708 return ERROR_NET_ADHOC_NOT_INITIALIZED;
3709 }
3710
3711 /**
3712 * Adhoc Emulator PTP Socket Closer
3713 * @param id Socket File Descriptor
3714 * @param flag Bitflags (Unused)
3715 * @return 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED
3716 */
sceNetAdhocPtpClose(int id,int unknown)3717 static int sceNetAdhocPtpClose(int id, int unknown) {
3718 INFO_LOG(SCENET,"sceNetAdhocPtpClose(%d,%d) at %08x",id,unknown,currentMIPS->pc);
3719 /*if (!g_Config.bEnableWlan) {
3720 return 0;
3721 }*/
3722
3723 return NetAdhocPtp_Close(id, unknown);
3724 }
3725
3726
3727 /**
3728 * Adhoc Emulator PTP Passive Socket Creator
3729 * @param saddr Local MAC (Unused)
3730 * @param sport Local Binding Port
3731 * @param bufsize Socket Buffer Size
3732 * @param rexmt_int Retransmit Interval (in Microseconds)
3733 * @param rexmt_cnt Retransmit Count
3734 * @param backlog Size of Connection Queue
3735 * @param flag Bitflags (Unused)
3736 * @return Socket ID > 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_ADDR, ADHOC_INVALID_PORT, ADHOC_SOCKET_ID_NOT_AVAIL, ADHOC_PORT_NOT_AVAIL, ADHOC_PORT_IN_USE, NET_NO_SPACE
3737 */
sceNetAdhocPtpListen(const char * srcmac,int sport,int bufsize,int rexmt_int,int rexmt_cnt,int backlog,int flag)3738 static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int rexmt_int, int rexmt_cnt, int backlog, int flag) {
3739 INFO_LOG(SCENET, "sceNetAdhocPtpListen(%s, %d, %d, %d, %d, %d, %d) at %08x", mac2str((SceNetEtherAddr*)srcmac).c_str(), sport,bufsize,rexmt_int,rexmt_cnt,backlog,flag, currentMIPS->pc);
3740 if (!g_Config.bEnableWlan) {
3741 return -1;
3742 }
3743 // Library is initialized
3744 SceNetEtherAddr * saddr = (SceNetEtherAddr *)srcmac;
3745 if (netAdhocInited) {
3746 // Some games (ie. DBZ Shin Budokai 2) might be getting the saddr/srcmac content from SaveState and causing problems :( So we try to fix it here
3747 if (saddr != NULL) {
3748 getLocalMac(saddr);
3749 }
3750 // Valid Address. FIXME: MAC only valid after successful attempt to Create/Connect/Join a Group? (ie. adhocctlCurrentMode != ADHOCCTL_MODE_NONE)
3751 if ((adhocctlCurrentMode != ADHOCCTL_MODE_NONE) && saddr != NULL && isLocalMAC(saddr)) {
3752 // It's allowed to Listen and Open the same PTP port, But it's not allowed to Listen or Open the same PTP port twice.
3753 if (isPTPPortInUse(sport, true)) {
3754 // FIXME: When PORT_IN_USE error occured it seems the index to the socket id also increased, which means it tries to create & bind the socket first and then closes it due to failed to bind
3755 return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_IN_USE, "port in use");
3756 }
3757
3758 // Random Port required
3759 if (sport == 0) {
3760 //sport 0 should be shifted back to 0 when using offset Phantasy Star Portable 2 use this
3761 sport = -static_cast<int>(portOffset);
3762 }
3763
3764 // Valid Arguments
3765 if (bufsize > 0 && rexmt_int > 0 && rexmt_cnt > 0 && backlog > 0)
3766 {
3767 // Create Infrastructure Socket
3768 int tcpsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
3769
3770 // Valid Socket produced
3771 if (tcpsocket > 0) {
3772 // Change socket MSS
3773 setSockMSS(tcpsocket, PSP_ADHOC_PTP_MSS);
3774
3775 // Change socket buffer size to be consistent on all platforms.
3776 setSockBufferSize(tcpsocket, SO_SNDBUF, bufsize*5); //PSP_ADHOC_PTP_MSS
3777 setSockBufferSize(tcpsocket, SO_RCVBUF, bufsize*10); //PSP_ADHOC_PTP_MSS*10
3778
3779 // Enable KeepAlive
3780 setSockKeepAlive(tcpsocket, true, rexmt_int / 1000000L, rexmt_cnt);
3781
3782 // Ignore SIGPIPE when supported (ie. BSD/MacOS)
3783 setSockNoSIGPIPE(tcpsocket, 1);
3784
3785 // Enable Port Re-use
3786 setSockReuseAddrPort(tcpsocket);
3787
3788 // Apply Default Receive Timeout Settings to Socket
3789 setSockTimeout(tcpsocket, SO_RCVTIMEO, rexmt_int);
3790
3791 // Disable Nagle Algo to send immediately. Or may be we shouldn't disable Nagle since there is PtpFlush function?
3792 setSockNoDelay(tcpsocket, 1);
3793
3794 // Binding Information for local Port
3795 struct sockaddr_in addr;
3796 addr.sin_family = AF_INET;
3797 addr.sin_addr.s_addr = INADDR_ANY;
3798 if (isLocalServer) {
3799 getLocalIp(&addr);
3800 }
3801 uint16_t requestedport = static_cast<int>(sport + static_cast<int>(portOffset));
3802 // Avoid getting random port due to port offset when original port wasn't 0 (ie. original_port + port_offset = 65536 = 0)
3803 if (requestedport == 0 && sport > 0)
3804 requestedport = 65535; // Hopefully it will be safe to default it to 65535 since there can't be more than one port that can bumped into 65536
3805 // Show a warning about privileged ports
3806 if (requestedport != 0 && requestedport < 1024) {
3807 WARN_LOG(SCENET, "sceNetAdhocPtpListen - Ports below 1024(ie. %hu) may require Admin Privileges", requestedport);
3808 }
3809 addr.sin_port = htons(requestedport);
3810
3811 int iResult = 0;
3812 // Bound Socket to local Port
3813 if ((iResult = bind(tcpsocket, (struct sockaddr*)&addr, sizeof(addr))) == 0) {
3814 // Update sport with the port assigned internal->lport = ntohs(local.sin_port)
3815 socklen_t len = sizeof(addr);
3816 if (getsockname(tcpsocket, (struct sockaddr*)&addr, &len) == 0) {
3817 uint16_t boundport = ntohs(addr.sin_port);
3818 if (sport + static_cast<int>(portOffset) >= 65536 || static_cast<int>(boundport) - static_cast<int>(portOffset) <= 0)
3819 WARN_LOG(SCENET, "sceNetAdhocPtpListen - Wrapped Port Detected: Original(%d) -> Requested(%d), Bound(%d) -> BoundOriginal(%d)", sport, requestedport, boundport, boundport - portOffset);
3820 sport = boundport - portOffset;
3821 }
3822 // Switch into Listening Mode
3823 if ((iResult = listen(tcpsocket, backlog)) == 0) {
3824 // Allocate Memory
3825 AdhocSocket* internal = (AdhocSocket*)malloc(sizeof(AdhocSocket));
3826
3827 // Allocated Memory
3828 if (internal != NULL) {
3829 // Find Free Translator ID
3830 // FIXME: We should probably use an increasing index instead of looking for an empty slot from beginning if we want to simulate a real socket id
3831 int i = 0;
3832 for (; i < MAX_SOCKET; i++) if (adhocSockets[i] == NULL) break;
3833
3834 // Found Free Translator ID
3835 if (i < MAX_SOCKET) {
3836 // Clear Memory
3837 memset(internal, 0, sizeof(AdhocSocket));
3838
3839 // Socket Type
3840 internal->type = SOCK_PTP;
3841 internal->retry_interval = rexmt_int;
3842 internal->retry_count = rexmt_cnt;
3843 internal->nonblocking = flag;
3844 internal->buffer_size = bufsize;
3845
3846 // Copy Infrastructure Socket ID
3847 internal->data.ptp.id = tcpsocket;
3848
3849 // Copy Address Information
3850 internal->data.ptp.laddr = *saddr;
3851 internal->data.ptp.lport = sport;
3852
3853 // Flag Socket as Listener
3854 internal->data.ptp.state = ADHOC_PTP_STATE_LISTEN;
3855
3856 // Link PTP Socket
3857 adhocSockets[i] = internal;
3858
3859 // Add Port Forward to Router
3860 //sceNetPortOpen("TCP", sport);
3861 UPnP_Add(IP_PROTOCOL_TCP, isOriPort ? sport : sport + portOffset, sport + portOffset);
3862
3863 // Switch to non-blocking for futher usage
3864 changeBlockingMode(tcpsocket, 1);
3865
3866 // Return PTP Socket Pointer
3867 return hleLogDebug(SCENET, i + 1, "success");
3868 }
3869
3870 // Free Memory
3871 free(internal);
3872 }
3873 }
3874 }
3875 else {
3876 auto n = GetI18NCategory("Networking");
3877 host->NotifyUserMessage(std::string(n->T("Failed to Bind Port")) + " " + std::to_string(sport + portOffset) + "\n" + std::string(n->T("Please change your Port Offset")), 3.0, 0x0000ff);
3878 }
3879
3880 if (iResult == SOCKET_ERROR) {
3881 int error = errno;
3882 ERROR_LOG(SCENET, "sceNetAdhocPtpListen[%i]: Socket Error (%i)", sport, error);
3883 }
3884
3885 // Close Socket
3886 closesocket(tcpsocket);
3887
3888 // Port not available (exclusively in use?)
3889 return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_NOT_AVAIL, "port not available"); //ERROR_NET_ADHOC_PORT_IN_USE; // ERROR_NET_ADHOC_INVALID_PORT;
3890 }
3891
3892 // Socket not available
3893 return hleLogDebug(SCENET, ERROR_NET_ADHOC_SOCKET_ID_NOT_AVAIL, "socket id not available");
3894 }
3895
3896 // Invalid Arguments
3897 return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg");
3898 }
3899
3900 // Invalid Addresses
3901 return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ADDR, "invalid address");
3902 }
3903
3904 // Library is uninitialized
3905 return hleLogDebug(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "adhoc not initialized");
3906 }
3907
3908 /**
3909 * Adhoc Emulator PTP Sender
3910 * @param id Socket File Descriptor
3911 * @param data Data Payload
3912 * @param len IN: Length of Payload OUT: Sent Data (in Bytes)
3913 * @param timeout Send Timeout (in Microseconds)
3914 * @param flag Nonblocking Flag
3915 * @return 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_NOT_CONNECTED, ADHOC_THREAD_ABORTED, ADHOC_INVALID_DATALEN, ADHOC_DISCONNECTED, NET_INTERNAL, NET_NO_SPACE
3916 */
sceNetAdhocPtpSend(int id,u32 dataAddr,u32 dataSizeAddr,int timeout,int flag)3917 static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeout, int flag) {
3918 DEBUG_LOG(SCENET, "sceNetAdhocPtpSend(%d,%08x,%08x,%d,%d) at %08x", id, dataAddr, dataSizeAddr, timeout, flag, currentMIPS->pc);
3919
3920 int * len = (int *)Memory::GetPointer(dataSizeAddr);
3921 const char * data = Memory::GetCharPointer(dataAddr);
3922 // Library is initialized
3923 if (netAdhocInited) {
3924 // Valid Socket
3925 if (id > 0 && id <= MAX_SOCKET && adhocSockets[id - 1] != NULL) {
3926 // Cast Socket
3927 auto socket = adhocSockets[id - 1];
3928 auto& ptpsocket = socket->data.ptp;
3929 socket->nonblocking = flag;
3930
3931 // Connected Socket
3932 if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED || ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT) {
3933 // Valid Arguments
3934 if (data != NULL && len != NULL && *len > 0) {
3935 // Schedule Timeout Removal
3936 //if (flag) timeout = 0; // JPCSP seems to always Send PTP as blocking, also a possibility to send to multiple destination?
3937
3938 // Apply Send Timeout Settings to Socket
3939 if (timeout > 0)
3940 setSockTimeout(ptpsocket.id, SO_SNDTIMEO, timeout);
3941
3942 if (socket->flags & ADHOC_F_ALERTSEND) {
3943 socket->alerted_flags |= ADHOC_F_ALERTSEND;
3944
3945 return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted");
3946 }
3947
3948 // Acquire Network Lock
3949 // _acquireNetworkLock();
3950
3951 // Send Data
3952 int sent = send(ptpsocket.id, data, *len, MSG_NOSIGNAL);
3953 int error = errno;
3954
3955 // Free Network Lock
3956 // _freeNetworkLock();
3957
3958 // Success
3959 if (sent > 0) {
3960 hleEatMicro(50); // mostly 1ms, sometimes 1~10ms ? doesn't seems to be switching to a different thread during this duration
3961 // Save Length
3962 *len = sent;
3963
3964 DEBUG_LOG(SCENET, "sceNetAdhocPtpSend[%i:%u]: Sent %u bytes to %s:%u\n", id, ptpsocket.lport, sent, mac2str(&ptpsocket.paddr).c_str(), ptpsocket.pport);
3965
3966 // Set to Established on successful Send when an attempt to Connect was initiated
3967 if (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT)
3968 ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED;
3969
3970 // Return Success
3971 return 0;
3972 }
3973
3974 // Non-Critical Error
3975 else if (sent == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK || (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT && (error == ENOTCONN || connectInProgress(error))))) {
3976 // Non-Blocking
3977 if (flag)
3978 return hleLogSuccessVerboseX(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block");
3979
3980 // Simulate blocking behaviour with non-blocking socket
3981 u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id;
3982 return WaitBlockingAdhocSocket(threadSocketId, PTP_SEND, id, (void*)data, len, timeout, nullptr, nullptr, "ptp send");
3983 }
3984
3985 DEBUG_LOG(SCENET, "sceNetAdhocPtpSend[%i:%u -> %s:%u]: Result:%i (Error:%i)", id, ptpsocket.lport, mac2str(&ptpsocket.paddr).c_str(), ptpsocket.pport, sent, error);
3986
3987 // Change Socket State
3988 ptpsocket.state = ADHOC_PTP_STATE_CLOSED;
3989
3990 // Disconnected or Not connected?
3991 return hleLogError(SCENET, ERROR_NET_ADHOC_DISCONNECTED, "disconnected?");
3992 }
3993
3994 // Invalid Arguments
3995 return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg");
3996 }
3997
3998 // Disconnected
3999 return hleLogError(SCENET, ERROR_NET_ADHOC_DISCONNECTED, "disconnected");
4000 }
4001
4002 // Invalid Socket
4003 return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_SOCKET_ID, "invalid socket id");
4004 }
4005
4006 // Library is uninitialized
4007 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "not initialized");
4008 }
4009
4010 /**
4011 * Adhoc Emulator PTP Receiver
4012 * @param id Socket File Descriptor
4013 * @param buf Data Buffer
4014 * @param len IN: Buffersize OUT: Received Data (in Bytes)
4015 * @param timeout Receive Timeout (in Microseconds)
4016 * @param flag Nonblocking Flag
4017 * @return 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_THREAD_ABORTED, ADHOC_DISCONNECTED, NET_INTERNAL
4018 */
sceNetAdhocPtpRecv(int id,u32 dataAddr,u32 dataSizeAddr,int timeout,int flag)4019 static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeout, int flag) {
4020 DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv(%d,%08x,%08x,%d,%d) at %08x", id, dataAddr, dataSizeAddr, timeout, flag, currentMIPS->pc);
4021
4022 void * buf = (void *)Memory::GetPointer(dataAddr);
4023 int * len = (int *)Memory::GetPointer(dataSizeAddr);
4024 // Library is initialized
4025 if (netAdhocInited) {
4026 // Valid Arguments
4027 if (buf != NULL && len != NULL && *len > 0) {
4028 // Valid Socket
4029 if (id > 0 && id <= MAX_SOCKET && adhocSockets[id - 1] != NULL) {
4030 // Cast Socket
4031 auto socket = adhocSockets[id - 1];
4032 auto& ptpsocket = socket->data.ptp;
4033 socket->nonblocking = flag;
4034
4035 if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED || ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT) {
4036 // Schedule Timeout Removal
4037 //if (flag) timeout = 0;
4038
4039 // Apply Receive Timeout Settings to Socket. Let's not wait forever (0 = indefinitely)
4040 if (timeout > 0)
4041 setSockTimeout(ptpsocket.id, SO_RCVTIMEO, timeout);
4042
4043 if (socket->flags & ADHOC_F_ALERTRECV) {
4044 socket->alerted_flags |= ADHOC_F_ALERTRECV;
4045
4046 return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted");
4047 }
4048
4049 // Acquire Network Lock
4050 // _acquireNetworkLock();
4051
4052 // TODO: Use a different thread (similar to sceIo) for recvfrom, recv & accept to prevent blocking-socket from blocking emulation
4053 int received = 0;
4054 int error = 0;
4055
4056 // Receive Data. POSIX: May received 0 bytes when the remote peer already closed the connection.
4057 received = recv(ptpsocket.id, (char*)buf, std::max(0, *len), MSG_NOSIGNAL);
4058 error = errno;
4059
4060 if (received == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK || (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT && (error == ENOTCONN || connectInProgress(error))))) {
4061 if (flag == 0) {
4062 // Simulate blocking behaviour with non-blocking socket
4063 u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id;
4064 return WaitBlockingAdhocSocket(threadSocketId, PTP_RECV, id, buf, len, timeout, nullptr, nullptr, "ptp recv");
4065 }
4066
4067 return hleLogSuccessVerboseX(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block");
4068 }
4069
4070 // Free Network Lock
4071 // _freeNetworkLock();
4072
4073 hleEatMicro(50);
4074
4075 // Received Data
4076 if (received > 0) {
4077 // Save Length
4078 *len = received;
4079
4080 // Update last recv timestamp, may cause disconnection not detected properly tho
4081 peerlock.lock();
4082 auto peer = findFriend(&ptpsocket.paddr);
4083 if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
4084 peerlock.unlock();
4085
4086 DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv[%i:%u]: Received %u bytes from %s:%u\n", id, ptpsocket.lport, received, mac2str(&ptpsocket.paddr).c_str(), ptpsocket.pport);
4087
4088 // Set to Established on successful Recv when an attempt to Connect was initiated
4089 if (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT)
4090 ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED;
4091
4092 // Return Success
4093 return 0;
4094 }
4095
4096 DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv[%i:%u]: Result:%i (Error:%i)", id, ptpsocket.lport, received, error);
4097
4098 if (*len == 0)
4099 return 0;
4100
4101 // Change Socket State
4102 ptpsocket.state = ADHOC_PTP_STATE_CLOSED;
4103
4104 // Disconnected or Not connected?
4105 return hleLogError(SCENET, ERROR_NET_ADHOC_DISCONNECTED, "disconnected?");
4106 }
4107
4108 return hleLogError(SCENET, ERROR_NET_ADHOC_DISCONNECTED, "disconnected");
4109 }
4110
4111 // Invalid Socket
4112 return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_SOCKET_ID, "invalid socket id");
4113 }
4114
4115 // Invalid Arguments
4116 return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid socket arg");
4117 }
4118
4119 // Library is uninitialized
4120 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "not initialized");
4121 }
4122
FlushPtpSocket(int socketId)4123 int FlushPtpSocket(int socketId) {
4124 // Get original Nagle algo value
4125 int n = getSockNoDelay(socketId);
4126
4127 // Disable Nagle Algo to send immediately
4128 setSockNoDelay(socketId, 1);
4129
4130 // Send Empty Data just to trigger Nagle on/off effect to flush the send buffer, Do we need to trigger this at all or is it automatically flushed?
4131 //changeBlockingMode(socket->id, nonblock);
4132 int ret = send(socketId, nullptr, 0, MSG_NOSIGNAL);
4133 if (ret == SOCKET_ERROR) ret = errno;
4134 //changeBlockingMode(socket->id, 1);
4135
4136 // Restore/Enable Nagle Algo
4137 setSockNoDelay(socketId, n);
4138
4139 return ret;
4140 }
4141
4142 /**
4143 * Adhoc Emulator PTP Flusher
4144 * @param id Socket File Descriptor
4145 * @param timeout Flush Timeout (in Microseconds)
4146 * @param flag Nonblocking Flag
4147 * @return 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_THREAD_ABORTED, ADHOC_DISCONNECTED, ADHOC_NOT_CONNECTED, NET_INTERNAL
4148 */
sceNetAdhocPtpFlush(int id,int timeout,int nonblock)4149 static int sceNetAdhocPtpFlush(int id, int timeout, int nonblock) {
4150 DEBUG_LOG(SCENET,"sceNetAdhocPtpFlush(%d,%d,%d) at %08x", id, timeout, nonblock, currentMIPS->pc);
4151
4152 // Library initialized
4153 if (netAdhocInited) {
4154 // Valid Socket
4155 if (id > 0 && id <= MAX_SOCKET && adhocSockets[id - 1] != NULL) {
4156 // Cast Socket
4157 auto socket = adhocSockets[id - 1];
4158 auto& ptpsocket = socket->data.ptp;
4159 socket->nonblocking = nonblock;
4160
4161 if (socket->flags & ADHOC_F_ALERTFLUSH) {
4162 socket->alerted_flags |= ADHOC_F_ALERTFLUSH;
4163
4164 return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted");
4165 }
4166
4167 // Connected Socket
4168 if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED) {
4169 hleEatMicro(50);
4170 // There are two ways to flush, you can either set TCP_NODELAY to 1 or TCP_CORK to 0.
4171 // Apply Send Timeout Settings to Socket
4172 setSockTimeout(ptpsocket.id, SO_SNDTIMEO, timeout);
4173
4174 int error = FlushPtpSocket(ptpsocket.id);
4175
4176 if (error == EAGAIN || error == EWOULDBLOCK) {
4177 // Non-Blocking
4178 if (nonblock)
4179 return hleLogSuccessVerboseX(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block");
4180
4181 // Simulate blocking behaviour with non-blocking socket
4182 u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id;
4183 return WaitBlockingAdhocSocket(threadSocketId, PTP_FLUSH, id, nullptr, nullptr, timeout, nullptr, nullptr, "ptp flush");
4184 }
4185 else if (isDisconnected(error)) {
4186 // Change Socket State
4187 ptpsocket.state = ADHOC_PTP_STATE_CLOSED;
4188
4189 // Disconnected
4190 return hleLogError(SCENET, ERROR_NET_ADHOC_DISCONNECTED, "disconnected");
4191 }
4192
4193 if (error != 0)
4194 DEBUG_LOG(SCENET, "sceNetAdhocPtpFlush[%i:%u -> %s:%u]: Error:%i", id, ptpsocket.lport, mac2str(&ptpsocket.paddr).c_str(), ptpsocket.pport, error);
4195 }
4196
4197 // Dummy Result, Always success?
4198 return 0;
4199 }
4200
4201 // Invalid Socket
4202 return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_SOCKET_ID, "invalid socket id");
4203 }
4204 // Library uninitialized
4205 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "not initialized");
4206 }
4207
4208 /**
4209 * Create own game object type data.
4210 *
4211 * @param dataAddr - A pointer to the game object data.
4212 * @param size - Size of the game data.
4213 *
4214 * @return 0 on success, < 0 on error.
4215 */
sceNetAdhocGameModeCreateMaster(u32 dataAddr,int size)4216 static int sceNetAdhocGameModeCreateMaster(u32 dataAddr, int size) {
4217 WARN_LOG(SCENET, "UNTESTED sceNetAdhocGameModeCreateMaster(%08x, %i) at %08x", dataAddr, size, currentMIPS->pc);
4218 if (!netAdhocctlInited)
4219 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized");
4220
4221 if (adhocctlCurrentMode != ADHOCCTL_MODE_GAMEMODE)
4222 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_IN_GAMEMODE, "not in gamemode");
4223
4224 if (!netAdhocGameModeEntered)
4225 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_ENTER_GAMEMODE, "not enter gamemode");
4226
4227 if (size < 0 || !Memory::IsValidAddress(dataAddr))
4228 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg");
4229
4230 hleEatMicro(1000);
4231 SceNetEtherAddr localMac;
4232 getLocalMac(&localMac);
4233 gameModeBuffSize = std::max(gameModeBuffSize, size);
4234
4235 u8* data = (u8*)malloc(size);
4236 if (data) {
4237 Memory::Memcpy(data, dataAddr, size);
4238 masterGameModeArea = { 0, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 1, 0, localMac, data };
4239 StartGameModeScheduler();
4240
4241 // Block current thread to sync initial master data after Master and all Replicas have been created
4242 if (replicaGameModeAreas.size() == (gameModeMacs.size() - 1)) {
4243 if (CoreTiming::IsScheduled(gameModeNotifyEvent)) {
4244 __KernelWaitCurThread(WAITTYPE_NET, GAMEMODE_WAITID, 0, 0, false, "syncing master data");
4245 DEBUG_LOG(SCENET, "GameMode: Blocking Thread %d to Sync initial Master data", __KernelGetCurThread());
4246 }
4247 }
4248 return hleLogDebug(SCENET, 0, "success"); // returned an id just like CreateReplica? always return 0?
4249 }
4250
4251 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CREATED, "not created");
4252 }
4253
4254 /**
4255 * Create peer game object type data.
4256 *
4257 * @param mac - The mac address of the peer.
4258 * @param dataAddr - A pointer to the game object data.
4259 * @param size - Size of the game data.
4260 *
4261 * @return The id of the replica on success, < 0 on error.
4262 */
sceNetAdhocGameModeCreateReplica(const char * mac,u32 dataAddr,int size)4263 static int sceNetAdhocGameModeCreateReplica(const char *mac, u32 dataAddr, int size) {
4264 WARN_LOG(SCENET, "UNTESTED sceNetAdhocGameModeCreateReplica(%s, %08x, %i) at %08x", mac2str((SceNetEtherAddr*)mac).c_str(), dataAddr, size, currentMIPS->pc);
4265 if (!netAdhocctlInited)
4266 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized");
4267
4268 if (adhocctlCurrentMode != ADHOCCTL_MODE_GAMEMODE)
4269 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_IN_GAMEMODE, "not in gamemode");
4270
4271 if (!netAdhocGameModeEntered)
4272 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_ENTER_GAMEMODE, "not enter gamemode");
4273
4274 if (mac == nullptr || size < 0 || !Memory::IsValidAddress(dataAddr))
4275 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg");
4276
4277 hleEatMicro(1000);
4278 int maxid = 0;
4279 auto it = std::find_if(replicaGameModeAreas.begin(), replicaGameModeAreas.end(),
4280 [mac, &maxid](GameModeArea const& e) {
4281 if (e.id > maxid) maxid = e.id;
4282 return IsMatch(e.mac, mac);
4283 });
4284 // MAC address already existed!
4285 if (it != replicaGameModeAreas.end()) {
4286 WARN_LOG(SCENET, "sceNetAdhocGameModeCreateReplica - [%s] is already existed (id: %d)", mac2str((SceNetEtherAddr*)mac).c_str(), it->id);
4287 return it->id; // ERROR_NET_ADHOC_ALREADY_CREATED
4288 }
4289
4290 int ret = 0;
4291 gameModeBuffSize = std::max(gameModeBuffSize, size);
4292
4293 u8* data = (u8*)malloc(size);
4294 if (data) {
4295 Memory::Memcpy(data, dataAddr, size);
4296 //int sock = sceNetAdhocPdpCreate(mac, ADHOC_GAMEMODE_PORT, size, 0);
4297 GameModeArea gma = { maxid + 1, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 0, 0, *(SceNetEtherAddr*)mac, data };
4298 replicaGameModeAreas.push_back(gma);
4299 ret = gma.id; // Valid id for replica is higher than 0?
4300
4301 // Block current thread to sync initial master data after Master and all Replicas have been created
4302 if (replicaGameModeAreas.size() == (gameModeMacs.size() - 1)) {
4303 if (CoreTiming::IsScheduled(gameModeNotifyEvent)) {
4304 __KernelWaitCurThread(WAITTYPE_NET, GAMEMODE_WAITID, ret, 0, false, "syncing master data");
4305 DEBUG_LOG(SCENET, "GameMode: Blocking Thread %d to Sync initial Master data", __KernelGetCurThread());
4306 }
4307 }
4308 return hleLogSuccessInfoI(SCENET, ret, "success");
4309 }
4310
4311 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CREATED, "not created");
4312 }
4313
sceNetAdhocGameModeUpdateMaster()4314 static int sceNetAdhocGameModeUpdateMaster() {
4315 DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocGameModeUpdateMaster() at %08x", currentMIPS->pc);
4316 if (!netAdhocctlInited)
4317 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized");
4318
4319 if (adhocctlCurrentMode != ADHOCCTL_MODE_GAMEMODE)
4320 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_IN_GAMEMODE, "not in gamemode");
4321
4322 if (!netAdhocGameModeEntered)
4323 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_ENTER_GAMEMODE, "not enter gamemode");
4324
4325 if (masterGameModeArea.data) {
4326 Memory::Memcpy(masterGameModeArea.data, masterGameModeArea.addr, masterGameModeArea.size);
4327 masterGameModeArea.dataUpdated = 1;
4328 masterGameModeArea.updateTimestamp = CoreTiming::GetGlobalTimeUsScaled();
4329 // Reset sent marker
4330 for (auto& gma : replicaGameModeAreas)
4331 gma.dataSent = 0;
4332 }
4333
4334 hleEatMicro(100);
4335 return 0;
4336 }
4337
NetAdhocGameMode_DeleteMaster()4338 int NetAdhocGameMode_DeleteMaster() {
4339 if (masterGameModeArea.data) {
4340 free(masterGameModeArea.data);
4341 }
4342 //NetAdhocPdp_Delete(masterGameModeArea.socket, 0);
4343 gameModePeerPorts.erase(masterGameModeArea.mac);
4344 masterGameModeArea = { 0 };
4345
4346 if (replicaGameModeAreas.size() <= 0) {
4347 NetAdhocPdp_Delete(gameModeSocket, 0);
4348 gameModeSocket = (int)INVALID_SOCKET;
4349 }
4350
4351 return 0;
4352 }
4353
sceNetAdhocGameModeDeleteMaster()4354 static int sceNetAdhocGameModeDeleteMaster() {
4355 WARN_LOG(SCENET, "UNTESTED sceNetAdhocGameModeDeleteMaster() at %08x", currentMIPS->pc);
4356 if (isZeroMAC(&masterGameModeArea.mac))
4357 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CREATED, "not created");
4358
4359 return NetAdhocGameMode_DeleteMaster();
4360 }
4361
sceNetAdhocGameModeUpdateReplica(int id,u32 infoAddr)4362 static int sceNetAdhocGameModeUpdateReplica(int id, u32 infoAddr) {
4363 DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocGameModeUpdateReplica(%i, %08x) at %08x", id, infoAddr, currentMIPS->pc);
4364 if (!netAdhocctlInited)
4365 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized");
4366
4367 if (adhocctlCurrentMode != ADHOCCTL_MODE_GAMEMODE)
4368 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_IN_GAMEMODE, "not in gamemode");
4369
4370 if (!netAdhocGameModeEntered)
4371 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_ENTER_GAMEMODE, "not enter gamemode");
4372
4373 auto it = std::find_if(replicaGameModeAreas.begin(), replicaGameModeAreas.end(),
4374 [id](GameModeArea const& e) {
4375 return e.id == id;
4376 });
4377
4378 if (it == replicaGameModeAreas.end())
4379 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CREATED, "not created");
4380
4381 for (auto gma : replicaGameModeAreas) {
4382 if (gma.id == id) {
4383 if (Memory::IsValidAddress(infoAddr)) {
4384 GameModeUpdateInfo* gmuinfo = (GameModeUpdateInfo*)Memory::GetPointer(infoAddr);
4385 gmuinfo->length = sizeof(GameModeUpdateInfo);
4386 if (gma.data && gma.dataUpdated) {
4387 Memory::Memcpy(gma.addr, gma.data, gma.size);
4388 gma.dataUpdated = 0;
4389 gmuinfo->updated = 1;
4390 gmuinfo->timeStamp = std::max(gma.updateTimestamp, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta);
4391 }
4392 else {
4393 gmuinfo->updated = 0;
4394 }
4395 }
4396 break;
4397 }
4398 }
4399
4400 hleEatMicro(100);
4401 return 0;
4402 }
4403
sceNetAdhocGameModeDeleteReplica(int id)4404 static int sceNetAdhocGameModeDeleteReplica(int id) {
4405 WARN_LOG(SCENET, "UNTESTED sceNetAdhocGameModeDeleteReplica(%i) at %08x", id, currentMIPS->pc);
4406 auto it = std::find_if(replicaGameModeAreas.begin(), replicaGameModeAreas.end(),
4407 [id](GameModeArea const& e) {
4408 return e.id == id;
4409 });
4410
4411 if (it == replicaGameModeAreas.end())
4412 return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CREATED, "not created");
4413
4414 if (it->data) {
4415 free(it->data);
4416 it->data = nullptr;
4417 }
4418 //sceNetAdhocPdpDelete(it->socket, 0);
4419 gameModePeerPorts.erase(it->mac);
4420 replicaGameModeAreas.erase(it);
4421
4422 if (replicaGameModeAreas.size() <= 0 && isZeroMAC(&masterGameModeArea.mac)) {
4423 //sceNetAdhocPdpDelete(gameModeSocket, 0);
4424 //gameModeSocket = (int)INVALID_SOCKET;
4425 }
4426
4427 return 0;
4428 }
4429
sceNetAdhocGetSocketAlert(int id,u32 flagPtr)4430 int sceNetAdhocGetSocketAlert(int id, u32 flagPtr) {
4431 WARN_LOG_REPORT_ONCE(sceNetAdhocGetSocketAlert, SCENET, "UNTESTED sceNetAdhocGetSocketAlert(%i, %08x) at %08x", id, flagPtr, currentMIPS->pc);
4432 if (!Memory::IsValidAddress(flagPtr))
4433 return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg");
4434
4435 if (id < 1 || id > MAX_SOCKET || adhocSockets[id - 1] == NULL)
4436 return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_SOCKET_ID, "invalid socket id");
4437
4438 s32_le flg = adhocSockets[id - 1]->flags;
4439 Memory::Write_U32(flg, flagPtr);
4440
4441 return hleLogDebug(SCENET, 0, "flags = %08x", flg);
4442 }
4443
NetAdhocMatching_Stop(int matchingId)4444 int NetAdhocMatching_Stop(int matchingId) {
4445 SceNetAdhocMatchingContext* item = findMatchingContext(matchingId);
4446
4447 if (item != NULL) {
4448 // This will cause using PdpRecv on this socket to return ERROR_NET_ADHOC_SOCKET_ALERTED (Based on Ys vs. Sora no Kiseki when tested with JPCSP + prx files). Is this used to abort inprogress socket activity?
4449 NetAdhoc_SetSocketAlert(item->socket, ADHOC_F_ALERTRECV);
4450
4451 item->inputRunning = false;
4452 if (item->inputThread.joinable()) {
4453 item->inputThread.join();
4454 }
4455
4456 item->eventRunning = false;
4457 if (item->eventThread.joinable()) {
4458 item->eventThread.join();
4459 }
4460
4461 // Stop fake PSP Thread.
4462 // kernelObjects may already been cleared early during a Shutdown, thus trying to access it may generates Warning/Error in the log
4463 if (matchingThreads[item->matching_thid] > 0 && strcmp(__KernelGetThreadName(matchingThreads[item->matching_thid]), "ERROR") != 0) {
4464 __KernelStopThread(matchingThreads[item->matching_thid], SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching stopped");
4465 __KernelDeleteThread(matchingThreads[item->matching_thid], SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching deleted");
4466 }
4467 matchingThreads[item->matching_thid] = 0;
4468
4469 // Make sure nobody locking/using the socket
4470 item->socketlock->lock();
4471 // Delete the socket
4472 NetAdhocPdp_Delete(item->socket, 0); // item->connected = (sceNetAdhocPdpDelete(item->socket, 0) < 0);
4473 item->socketlock->unlock();
4474
4475 // Multithreading Lock
4476 peerlock.lock();
4477
4478 // Remove your own MAC, or All members, or don't remove at all or we should do this on MatchingDelete ?
4479 clearPeerList(item); //deleteAllMembers(item);
4480
4481 item->running = 0;
4482 netAdhocMatchingStarted--;
4483
4484 // Multithreading Unlock
4485 peerlock.unlock();
4486
4487 }
4488
4489 return 0;
4490 }
4491
sceNetAdhocMatchingStop(int matchingId)4492 int sceNetAdhocMatchingStop(int matchingId) {
4493 WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingStop(%i) at %08x", matchingId, currentMIPS->pc);
4494
4495 return NetAdhocMatching_Stop(matchingId);
4496 }
4497
NetAdhocMatching_Delete(int matchingId)4498 int NetAdhocMatching_Delete(int matchingId) {
4499 // Previous Context Reference
4500 SceNetAdhocMatchingContext* prev = NULL;
4501
4502 // Multithreading Lock
4503 peerlock.lock(); //contextlock.lock();
4504
4505 // Context Pointer
4506 SceNetAdhocMatchingContext* item = contexts;
4507
4508 // Iterate contexts
4509 for (; item != NULL; item = item->next) {
4510 // Found matching ID
4511 if (item->id == matchingId) {
4512 // Unlink Left (Beginning)
4513 if (prev == NULL) contexts = item->next;
4514
4515 // Unlink Left (Other)
4516 else prev->next = item->next;
4517
4518 // Stop it first if it's still running
4519 if (item->running) {
4520 NetAdhocMatching_Stop(matchingId);
4521 }
4522 // Delete the Fake PSP Thread
4523 //__KernelDeleteThread(item->matching_thid, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching deleted");
4524 //delete item->matchingThread;
4525
4526 // Free allocated memories
4527 free(item->hello);
4528 free(item->rxbuf);
4529 clearPeerList(item); //deleteAllMembers(item);
4530 (*item->peerPort).clear();
4531 delete item->peerPort;
4532 // Destroy locks
4533 item->eventlock->lock(); // Make sure it's not locked when being deleted
4534 item->eventlock->unlock();
4535 delete item->eventlock;
4536 item->inputlock->lock(); // Make sure it's not locked when being deleted
4537 item->inputlock->unlock();
4538 delete item->inputlock;
4539 item->socketlock->lock(); // Make sure it's not locked when being deleted
4540 item->socketlock->unlock();
4541 delete item->socketlock;
4542 // Free item context memory
4543 free(item);
4544 item = NULL;
4545
4546 // Making sure there are no leftover matching events from this session which could cause a crash on the next session
4547 deleteMatchingEvents(matchingId);
4548
4549 // Stop Search
4550 break;
4551 }
4552
4553 // Set Previous Reference
4554 prev = item;
4555 }
4556
4557 // Multithreading Unlock
4558 peerlock.unlock(); //contextlock.unlock();
4559
4560 return 0;
4561 }
4562
sceNetAdhocMatchingDelete(int matchingId)4563 int sceNetAdhocMatchingDelete(int matchingId) {
4564 // WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right?
4565
4566 NetAdhocMatching_Delete(matchingId);
4567
4568 WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingDelete(%i) at %08x", matchingId, currentMIPS->pc);
4569
4570 // Give a little time to make sure everything are cleaned up before the following AdhocMatchingCreate, Not too long tho, otherwise Naruto Ultimate Ninja Heroes 3 will have an issue
4571 //hleDelayResult(0, "give time to init/cleanup", adhocExtraPollDelayMS * 1000);
4572 return 0;
4573 }
4574
sceNetAdhocMatchingInit(u32 memsize)4575 int sceNetAdhocMatchingInit(u32 memsize) {
4576 WARN_LOG(SCENET, "sceNetAdhocMatchingInit(%d) at %08x", memsize, currentMIPS->pc);
4577
4578 // Uninitialized Library
4579 if (netAdhocMatchingInited)
4580 return ERROR_NET_ADHOC_MATCHING_ALREADY_INITIALIZED;
4581
4582 // Save Fake Pool Size
4583 fakePoolSize = memsize;
4584
4585 // Initialize Library
4586 deleteMatchingEvents();
4587 netAdhocMatchingInited = true;
4588
4589 // Return Success
4590 return 0;
4591 }
4592
NetAdhocMatching_Term()4593 int NetAdhocMatching_Term() {
4594 if (netAdhocMatchingInited) {
4595 // Delete all Matching contexts
4596 SceNetAdhocMatchingContext* next = NULL;
4597 SceNetAdhocMatchingContext* context = contexts;
4598 while (context != NULL) {
4599 next = context->next;
4600 //if (context->running) NetAdhocMatching_Stop(context->id);
4601 NetAdhocMatching_Delete(context->id);
4602 context = next;
4603 }
4604 contexts = NULL;
4605 matchingThreads.clear();
4606 }
4607
4608 return 0;
4609 }
4610
sceNetAdhocMatchingTerm()4611 int sceNetAdhocMatchingTerm() {
4612 WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingTerm() at %08x", currentMIPS->pc);
4613 // Should we cleanup all created matching contexts first? just in case there are games that doesn't delete them before calling this
4614 NetAdhocMatching_Term();
4615
4616 netAdhocMatchingInited = false;
4617 return 0;
4618 }
4619
4620
4621 // Presumably returns a "matchingId".
sceNetAdhocMatchingCreate(int mode,int maxnum,int port,int rxbuflen,int hello_int,int keepalive_int,int init_count,int rexmt_int,u32 callbackAddr)4622 static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbuflen, int hello_int, int keepalive_int, int init_count, int rexmt_int, u32 callbackAddr) {
4623 WARN_LOG(SCENET, "sceNetAdhocMatchingCreate(mode=%i, maxnum=%i, port=%i, rxbuflen=%i, hello=%i, keepalive=%i, initcount=%i, rexmt=%i, callbackAddr=%08x) at %08x", mode, maxnum, port, rxbuflen, hello_int, keepalive_int, init_count, rexmt_int, callbackAddr, currentMIPS->pc);
4624 if (!g_Config.bEnableWlan) {
4625 return -1;
4626 }
4627
4628 SceNetAdhocMatchingHandler handler;
4629 handler.entryPoint = callbackAddr;
4630
4631 // Library initialized
4632 if (netAdhocMatchingInited) {
4633 // Valid Member Limit
4634 if (maxnum > 1 && maxnum <= 16) {
4635 // Valid Receive Buffer size
4636 if (rxbuflen >= 1) { //1024 //200 on DBZ Shin Budokai 2
4637 // Valid Arguments
4638 if (mode >= 1 && mode <= 3) {
4639
4640 // Iterate Matching Contexts
4641 SceNetAdhocMatchingContext * item = contexts;
4642 for (; item != NULL; item = item->next) {
4643 // Port Match found
4644 if (item->port == port)
4645 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_PORT_IN_USE, "adhoc matching port in use");
4646 }
4647
4648 // Allocate Context Memory
4649 SceNetAdhocMatchingContext * context = (SceNetAdhocMatchingContext *)malloc(sizeof(SceNetAdhocMatchingContext));
4650
4651 // Allocated Memory
4652 if (context != NULL) {
4653 // Create PDP Socket
4654 SceNetEtherAddr localmac;
4655 getLocalMac(&localmac);
4656
4657 // Clear Memory
4658 memset(context, 0, sizeof(SceNetAdhocMatchingContext));
4659
4660 // Allocate Receive Buffer
4661 context->rxbuf = (uint8_t*)malloc(rxbuflen);
4662
4663 // Allocated Memory
4664 if (context->rxbuf != NULL) {
4665 // Clear Memory
4666 memset(context->rxbuf, 0, rxbuflen);
4667
4668 // Fill in Context Data
4669 context->id = findFreeMatchingID();
4670 context->mode = mode;
4671 context->maxpeers = maxnum;
4672 context->port = port;
4673 context->rxbuflen = rxbuflen;
4674 context->resendcounter = init_count;
4675 context->resend_int = rexmt_int; // used as ack timeout on lost packet (ie. not receiving anything after sending)?
4676 context->hello_int = hello_int; // client might set this to 0
4677 if (keepalive_int < 1) context->keepalive_int = PSP_ADHOCCTL_PING_TIMEOUT; else context->keepalive_int = keepalive_int; // client might set this to 0
4678 context->keepalivecounter = init_count; // used to multiply keepalive_int as timeout
4679 context->timeout = (((u64)(keepalive_int)+(u64)rexmt_int) * (u64)init_count);
4680 context->timeout += adhocDefaultTimeout; // For internet play we need higher timeout than what the game wanted
4681 context->handler = handler;
4682 context->peerPort = new std::map<SceNetEtherAddr, u16_le>();
4683
4684 // Fill in Selfpeer
4685 context->mac = localmac;
4686
4687 // Create locks
4688 context->socketlock = new std::recursive_mutex;
4689 context->eventlock = new std::recursive_mutex;
4690 context->inputlock = new std::recursive_mutex;
4691
4692 // Multithreading Lock
4693 peerlock.lock(); //contextlock.lock();
4694
4695 // Add Callback Handler
4696 context->handler.entryPoint = callbackAddr;
4697 context->matching_thid = static_cast<int>(matchingThreads.size());
4698 matchingThreads.push_back(0);
4699
4700 // Link Context
4701 //context->connected = true;
4702 context->next = contexts;
4703 contexts = context;
4704
4705 // Multithreading UnLock
4706 peerlock.unlock(); //contextlock.unlock();
4707
4708 // Just to make sure Adhoc is already connected
4709 //hleDelayResult(context->id, "give time to init/cleanup", adhocEventDelayMS * 1000);
4710
4711 // Return Matching ID
4712 return hleLogDebug(SCENET, context->id, "success");
4713 }
4714
4715 // Free Memory
4716 free(context);
4717 }
4718
4719 // Out of Memory
4720 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NO_SPACE, "adhoc matching no space");
4721 }
4722
4723 // InvalidERROR_NET_Arguments
4724 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ARG, "adhoc matching invalid arg");
4725 }
4726
4727 // Invalid Receive Buffer Size
4728 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_RXBUF_TOO_SHORT, "adhoc matching rxbuf too short");
4729 }
4730
4731 // Invalid Member Limit
4732 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_MAXNUM, "adhoc matching invalid maxnum");
4733 }
4734 // Uninitialized Library
4735 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhoc matching not initialized");
4736 }
4737
NetAdhocMatching_Start(int matchingId,int evthPri,int evthPartitionId,int evthStack,int inthPri,int inthPartitionId,int inthStack,int optLen,u32 optDataAddr)4738 int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int evthStack, int inthPri, int inthPartitionId, int inthStack, int optLen, u32 optDataAddr) {
4739 // Multithreading Lock
4740 peerlock.lock();
4741
4742 SceNetAdhocMatchingContext* item = findMatchingContext(matchingId);
4743
4744 if (item != NULL) {
4745 //sceNetAdhocMatchingSetHelloOpt(matchingId, optLen, optDataAddr); //SetHelloOpt only works when context is running
4746 if ((optLen > 0) && Memory::IsValidAddress(optDataAddr)) {
4747 // Allocate the memory and copy the content
4748 if (item->hello != NULL) free(item->hello);
4749 item->hello = (uint8_t*)malloc(optLen);
4750 if (item->hello != NULL) {
4751 Memory::Memcpy(item->hello, optDataAddr, optLen);
4752 item->hellolen = optLen;
4753 item->helloAddr = optDataAddr;
4754 }
4755 //else return ERROR_NET_ADHOC_MATCHING_NO_SPACE; //Faking success to prevent GTA:VCS from stuck unable to choose host/join menu
4756 }
4757 //else return ERROR_NET_ADHOC_MATCHING_INVALID_ARG; // ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN; // Returning Not Success will cause GTA:VC stuck unable to choose host/join menu
4758
4759 // Create PDP Socket
4760 int sock = sceNetAdhocPdpCreate((const char*)&item->mac, static_cast<int>(item->port), item->rxbuflen, 0);
4761 item->socket = sock;
4762 if (sock < 1) {
4763 peerlock.unlock();
4764 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_PORT_IN_USE, "adhoc matching port in use");
4765 }
4766
4767 // Create & Start the Fake PSP Thread ("matching_ev%d" and "matching_io%d")
4768 netAdhocValidateLoopMemory();
4769 std::string thrname = std::string("MatchingThr") + std::to_string(matchingId);
4770 matchingThreads[item->matching_thid] = sceKernelCreateThread(thrname.c_str(), matchingThreadHackAddr, evthPri, evthStack, 0, 0);
4771 //item->matchingThread = new HLEHelperThread(thrname.c_str(), "sceNetAdhocMatching", "__NetMatchingCallbacks", inthPri, inthStack);
4772 if (matchingThreads[item->matching_thid] > 0) {
4773 sceKernelStartThread(matchingThreads[item->matching_thid], 0, 0); //sceKernelStartThread(context->event_thid, sizeof(context), &context);
4774 //item->matchingThread->Start(matchingId, 0);
4775 }
4776
4777 //Create the threads
4778 if (!item->eventRunning) {
4779 item->eventRunning = true;
4780 item->eventThread = std::thread(matchingEventThread, matchingId);
4781 }
4782 if (!item->inputRunning) {
4783 item->inputRunning = true;
4784 item->inputThread = std::thread(matchingInputThread, matchingId);
4785 }
4786
4787 item->running = 1;
4788 netAdhocMatchingStarted++;
4789 }
4790 //else return ERROR_NET_ADHOC_MATCHING_INVALID_ID; //Faking success to prevent GTA:VCS from stuck unable to choose host/join menu
4791
4792 // Multithreading Unlock
4793 peerlock.unlock();
4794
4795 return 0;
4796 }
4797
4798 #define KERNEL_PARTITION_ID 1
4799 #define USER_PARTITION_ID 2
4800 #define VSHELL_PARTITION_ID 5
4801 // This should be similar with sceNetAdhocMatchingStart2 but using USER_PARTITION_ID (2) for PartitionId params
sceNetAdhocMatchingStart(int matchingId,int evthPri,int evthStack,int inthPri,int inthStack,int optLen,u32 optDataAddr)4802 static int sceNetAdhocMatchingStart(int matchingId, int evthPri, int evthStack, int inthPri, int inthStack, int optLen, u32 optDataAddr) {
4803 WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingStart(%i, %i, %i, %i, %i, %i, %08x) at %08x", matchingId, evthPri, evthStack, inthPri, inthStack, optLen, optDataAddr, currentMIPS->pc);
4804 if (!g_Config.bEnableWlan)
4805 return -1;
4806
4807 int retval = NetAdhocMatching_Start(matchingId, evthPri, USER_PARTITION_ID, evthStack, inthPri, USER_PARTITION_ID, inthStack, optLen, optDataAddr);
4808 // Give a little time to make sure matching Threads are ready before the game use the next sceNet functions, should've checked for status instead of guessing the time?
4809 return hleDelayResult(retval, "give some time", adhocMatchingEventDelay);
4810 }
4811
4812 // With params for Partition ID for the event & input handler stack
sceNetAdhocMatchingStart2(int matchingId,int evthPri,int evthPartitionId,int evthStack,int inthPri,int inthPartitionId,int inthStack,int optLen,u32 optDataAddr)4813 static int sceNetAdhocMatchingStart2(int matchingId, int evthPri, int evthPartitionId, int evthStack, int inthPri, int inthPartitionId, int inthStack, int optLen, u32 optDataAddr) {
4814 WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingStart2(%i, %i, %i, %i, %i, %i, %i, %i, %08x) at %08x", matchingId, evthPri, evthPartitionId, evthStack, inthPri, inthPartitionId, inthStack, optLen, optDataAddr, currentMIPS->pc);
4815 if (!g_Config.bEnableWlan)
4816 return -1;
4817
4818 int retval = NetAdhocMatching_Start(matchingId, evthPri, evthPartitionId, evthStack, inthPri, inthPartitionId, inthStack, optLen, optDataAddr);
4819 // Give a little time to make sure matching Threads are ready before the game use the next sceNet functions, should've checked for status instead of guessing the time?
4820 return hleDelayResult(retval, "give some time", adhocMatchingEventDelay);
4821 }
4822
4823
sceNetAdhocMatchingSelectTarget(int matchingId,const char * macAddress,int optLen,u32 optDataPtr)4824 static int sceNetAdhocMatchingSelectTarget(int matchingId, const char *macAddress, int optLen, u32 optDataPtr) {
4825 WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingSelectTarget(%i, %s, %i, %08x) at %08x", matchingId, mac2str((SceNetEtherAddr*)macAddress).c_str(), optLen, optDataPtr, currentMIPS->pc);
4826 if (!g_Config.bEnableWlan)
4827 return -1;
4828
4829 // Initialized Library
4830 if (netAdhocMatchingInited)
4831 {
4832 // Valid Arguments
4833 if (macAddress != NULL)
4834 {
4835 SceNetEtherAddr * target = (SceNetEtherAddr *)macAddress;
4836
4837 // Find Matching Context for ID
4838 SceNetAdhocMatchingContext * context = findMatchingContext(matchingId);
4839
4840 // Found Matching Context
4841 if (context != NULL)
4842 {
4843 // Running Context
4844 if (context->running)
4845 {
4846 // Search Result
4847 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, (SceNetEtherAddr *)target);
4848
4849 // Found Peer in List
4850 if (peer != NULL)
4851 {
4852 // Valid Optional Data Length
4853 if ((optLen == 0) || (optLen > 0 && optDataPtr != 0))
4854 {
4855 void * opt = NULL;
4856 if (Memory::IsValidAddress(optDataPtr)) opt = Memory::GetPointer(optDataPtr);
4857 // Host Mode
4858 if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT)
4859 {
4860 // Already Connected
4861 if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_ALREADY_ESTABLISHED, "adhocmatching already established");
4862
4863 // Not enough space
4864 if (countChildren(context) == (context->maxpeers - 1)) return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_EXCEED_MAXNUM, "adhocmatching exceed maxnum");
4865
4866 // Requesting Peer
4867 if (peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST)
4868 {
4869 // Accept Peer in Group
4870 peer->state = PSP_ADHOC_MATCHING_PEER_CHILD;
4871
4872 // Sending order may need to be reversed since Stack appends to the front, so the order will be switched around.
4873
4874 // Tell Children about new Sibling
4875 sendBirthMessage(context, peer);
4876
4877 // Spawn Established Event
4878 //spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_ESTABLISHED, target, 0, NULL);
4879
4880 // Send Accept Confirmation to Peer
4881 sendAcceptMessage(context, peer, optLen, opt);
4882
4883 // Return Success
4884 return 0;
4885 }
4886 }
4887
4888 // Client Mode
4889 else if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD)
4890 {
4891 // Already connected
4892 if (findParent(context) != NULL) return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_ALREADY_ESTABLISHED, "adhocmatching already established");
4893
4894 // Outgoing Request in Progress
4895 if (findOutgoingRequest(context) != NULL) return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_REQUEST_IN_PROGRESS, "adhocmatching request in progress");
4896
4897 // Valid Offer
4898 if (peer->state == PSP_ADHOC_MATCHING_PEER_OFFER)
4899 {
4900 // Switch into Join Request Mode
4901 peer->state = PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST;
4902
4903 // Send Join Request to Peer
4904 sendJoinRequest(context, peer, optLen, opt);
4905
4906 // Return Success
4907 return 0;
4908 }
4909 }
4910
4911 // P2P Mode
4912 else
4913 {
4914 // Already connected
4915 if (findP2P(context) != NULL) return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_ALREADY_ESTABLISHED, "adhocmatching already established");
4916
4917 // Outgoing Request in Progress
4918 if (findOutgoingRequest(context) != NULL) return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_REQUEST_IN_PROGRESS, "adhocmatching request in progress");
4919
4920 // Join Request Mode
4921 if (peer->state == PSP_ADHOC_MATCHING_PEER_OFFER)
4922 {
4923 // Switch into Join Request Mode
4924 peer->state = PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST;
4925
4926 // Send Join Request to Peer
4927 sendJoinRequest(context, peer, optLen, opt);
4928
4929 // Return Success
4930 return 0;
4931 }
4932
4933 // Requesting Peer
4934 else if (peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST)
4935 {
4936 // Accept Peer in Group
4937 peer->state = PSP_ADHOC_MATCHING_PEER_P2P;
4938
4939 // Tell Children about new Sibling
4940 //sendBirthMessage(context, peer);
4941 // Send Accept Confirmation to Peer
4942 sendAcceptMessage(context, peer, optLen, opt);
4943
4944 // Return Success
4945 return 0;
4946 }
4947 }
4948
4949 // How did this happen?! It shouldn't!
4950 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_TARGET_NOT_READY, "adhocmatching target not ready");
4951 }
4952
4953 // Invalid Optional Data Length
4954 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN, "adhocmatching invalid optlen");
4955 }
4956
4957 // Peer not found
4958 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_UNKNOWN_TARGET, "adhocmatching unknown target");
4959 }
4960
4961 // Idle Context
4962 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_RUNNING, "adhocmatching not running");
4963 }
4964
4965 // Invalid Matching ID
4966 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ID, "adhocmatching invalid id");
4967 }
4968
4969 // Invalid Arguments
4970 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ARG, "adhocmatching invalid arg");
4971 }
4972
4973 // Uninitialized Library
4974 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhocmatching not initialized");
4975 }
4976
NetAdhocMatching_CancelTargetWithOpt(int matchingId,const char * macAddress,int optLen,u32 optDataPtr)4977 int NetAdhocMatching_CancelTargetWithOpt(int matchingId, const char* macAddress, int optLen, u32 optDataPtr) {
4978 // Initialized Library
4979 if (netAdhocMatchingInited)
4980 {
4981 SceNetEtherAddr* target = (SceNetEtherAddr*)macAddress;
4982 void* opt = NULL;
4983 if (Memory::IsValidAddress(optDataPtr)) opt = Memory::GetPointer(optDataPtr);
4984
4985 // Valid Arguments
4986 if (target != NULL && ((optLen == 0) || (optLen > 0 && opt != NULL)))
4987 {
4988 // Find Matching Context
4989 SceNetAdhocMatchingContext* context = findMatchingContext(matchingId);
4990
4991 // Found Matching Context
4992 if (context != NULL)
4993 {
4994 // Running Context
4995 if (context->running)
4996 {
4997 // Find Peer
4998 SceNetAdhocMatchingMemberInternal* peer = findPeer(context, (SceNetEtherAddr*)target);
4999
5000 // Found Peer
5001 if (peer != NULL)
5002 {
5003 // Valid Peer Mode
5004 if ((context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && (peer->state == PSP_ADHOC_MATCHING_PEER_PARENT || peer->state == PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST)) ||
5005 (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD || peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST)) ||
5006 (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && (peer->state == PSP_ADHOC_MATCHING_PEER_P2P || peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST)))
5007 {
5008 // Notify other Children of Death
5009 if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && peer->state == PSP_ADHOC_MATCHING_PEER_CHILD && countConnectedPeers(context) > 1)
5010 {
5011 // Send Death Message
5012 sendDeathMessage(context, peer);
5013 }
5014
5015 // Mark Peer as Canceled
5016 peer->state = PSP_ADHOC_MATCHING_PEER_CANCEL_IN_PROGRESS;
5017
5018 // Send Cancel Event to Peer
5019 sendCancelMessage(context, peer, optLen, opt);
5020
5021 // Delete Peer from List
5022 // Can't delete here, Threads still need this data.
5023 // deletePeer(context, peer);
5024 // Marking peer to be timedout instead of deleting immediately
5025 peer->lastping = 0;
5026
5027 hleEatCycles(adhocDefaultDelay);
5028 // Return Success
5029 return 0;
5030 }
5031 }
5032
5033 // Peer not found
5034 //return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_UNKNOWN_TARGET, "adhocmatching unknown target");
5035 // Faking success to prevent the game (ie. Soul Calibur) to repeatedly calling this function when the other player is disconnected
5036 return 0;
5037 }
5038
5039 // Context not running
5040 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_RUNNING, "adhocmatching not running");
5041 }
5042
5043 // Invalid Matching ID
5044 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ID, "adhocmatching invalid id");
5045 }
5046
5047 // Invalid Arguments
5048 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ARG, "adhocmatching invalid arg");
5049 }
5050
5051 // Uninitialized Library
5052 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhocmatching not initialized");
5053 }
5054
sceNetAdhocMatchingCancelTargetWithOpt(int matchingId,const char * macAddress,int optLen,u32 optDataPtr)5055 int sceNetAdhocMatchingCancelTargetWithOpt(int matchingId, const char *macAddress, int optLen, u32 optDataPtr) {
5056 WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingCancelTargetWithOpt(%i, %s, %i, %08x) at %08x", matchingId, mac2str((SceNetEtherAddr*)macAddress).c_str(), optLen, optDataPtr, currentMIPS->pc);
5057 if (!g_Config.bEnableWlan)
5058 return -1;
5059 return NetAdhocMatching_CancelTargetWithOpt(matchingId, macAddress, optLen, optDataPtr);
5060 }
5061
sceNetAdhocMatchingCancelTarget(int matchingId,const char * macAddress)5062 int sceNetAdhocMatchingCancelTarget(int matchingId, const char *macAddress) {
5063 WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingCancelTarget(%i, %s)", matchingId, mac2str((SceNetEtherAddr*)macAddress).c_str());
5064 if (!g_Config.bEnableWlan)
5065 return -1;
5066 return NetAdhocMatching_CancelTargetWithOpt(matchingId, macAddress, 0, 0);
5067 }
5068
sceNetAdhocMatchingGetHelloOpt(int matchingId,u32 optLenAddr,u32 optDataAddr)5069 int sceNetAdhocMatchingGetHelloOpt(int matchingId, u32 optLenAddr, u32 optDataAddr) {
5070 WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingGetHelloOpt(%i, %08x, %08x)", matchingId, optLenAddr, optDataAddr);
5071 if (!g_Config.bEnableWlan)
5072 return -1;
5073
5074 if (!Memory::IsValidAddress(optLenAddr)) return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
5075
5076 s32_le *optlen = PSPPointer<s32_le>::Create(optLenAddr);
5077
5078 // Multithreading Lock
5079 peerlock.lock();
5080
5081 SceNetAdhocMatchingContext * item = findMatchingContext(matchingId);
5082
5083 if (item != NULL) {
5084 // Get OptData
5085 *optlen = item->hellolen;
5086 if ((*optlen > 0) && Memory::IsValidAddress(optDataAddr)) {
5087 uint8_t * optdata = Memory::GetPointer(optDataAddr);
5088 memcpy(optdata, item->hello, *optlen);
5089 }
5090 //else return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
5091 }
5092 //else return ERROR_NET_ADHOC_MATCHING_INVALID_ID;
5093
5094 // Multithreading Unlock
5095 peerlock.unlock();
5096
5097 return 0;
5098 }
5099
sceNetAdhocMatchingSetHelloOpt(int matchingId,int optLenAddr,u32 optDataAddr)5100 int sceNetAdhocMatchingSetHelloOpt(int matchingId, int optLenAddr, u32 optDataAddr) {
5101 VERBOSE_LOG(SCENET, "UNTESTED sceNetAdhocMatchingSetHelloOpt(%i, %i, %08x) at %08x", matchingId, optLenAddr, optDataAddr, currentMIPS->pc);
5102 if (!g_Config.bEnableWlan)
5103 return -1;
5104
5105 if (!netAdhocMatchingInited)
5106 return hleLogDebug(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhocmatching not initialized");
5107
5108 // Multithreading Lock
5109 peerlock.lock();
5110
5111 SceNetAdhocMatchingContext* context = findMatchingContext(matchingId);
5112
5113 // Multithreading Unlock
5114 peerlock.unlock();
5115
5116 // Context not found
5117 if (context == NULL)
5118 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ID, "adhocmatching invalid id");
5119
5120 // Invalid Matching Mode (Child)
5121 if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD)
5122 return hleLogDebug(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_MODE, "adhocmatching invalid mode");
5123
5124 // Context not running
5125 if (!context->running)
5126 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_RUNNING, "adhocmatching not running");
5127
5128 // Invalid Optional Data Length
5129 if ((optLenAddr != 0) && (optDataAddr == 0))
5130 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN, "adhocmatching invalid optlen"); //ERROR_NET_ADHOC_MATCHING_INVALID_ARG
5131
5132 // Grab Existing Hello Data
5133 void* hello = context->hello;
5134
5135 // Free Previous Hello Data, or Reuse it
5136 //free(hello);
5137
5138 // Allocation Required
5139 if (optLenAddr > 0)
5140 {
5141 // Allocate Memory
5142 if (optLenAddr > context->hellolen) {
5143 hello = realloc(hello, optLenAddr);
5144 }
5145
5146 // Out of Memory
5147 if (hello == NULL) {
5148 context->hellolen = 0;
5149 return ERROR_NET_ADHOC_MATCHING_NO_SPACE;
5150 }
5151
5152 // Clone Hello Data
5153 Memory::Memcpy(hello, optDataAddr, optLenAddr);
5154
5155 // Set Hello Data
5156 context->hello = (uint8_t*)hello;
5157 context->hellolen = optLenAddr;
5158 context->helloAddr = optDataAddr;
5159 }
5160 else
5161 {
5162 // Delete Hello Data
5163 context->hellolen = 0;
5164 context->helloAddr = 0;
5165 //free(context->hello); // Doesn't need to free it since it will be reused later
5166 //context->hello = NULL;
5167 }
5168
5169 // Return Success
5170 return 0;
5171 }
5172
sceNetAdhocMatchingGetMembers(int matchingId,u32 sizeAddr,u32 buf)5173 static int sceNetAdhocMatchingGetMembers(int matchingId, u32 sizeAddr, u32 buf) {
5174 DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocMatchingGetMembers(%i, [%08x]=%i, %08x) at %08x", matchingId, sizeAddr, Memory::Read_U32(sizeAddr), buf, currentMIPS->pc);
5175 if (!g_Config.bEnableWlan)
5176 return -1;
5177
5178 if (!netAdhocMatchingInited)
5179 return hleLogDebug(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhocmatching not initialized");
5180
5181 // Minimum Argument
5182 if (!Memory::IsValidAddress(sizeAddr))
5183 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ARG, "adhocmatching invalid arg");
5184
5185 // Multithreading Lock
5186 peerlock.lock();
5187 // Find Matching Context
5188 SceNetAdhocMatchingContext* context = findMatchingContext(matchingId);
5189 // Multithreading Unlock
5190 peerlock.unlock();
5191
5192 // Context not found
5193 if (context == NULL)
5194 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ID, "adhocmatching invalid id");
5195
5196 // Context not running
5197 if (!context->running)
5198 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_RUNNING, "adhocmatching not running");
5199
5200 // Buffer Length not available
5201 if (!Memory::IsValidAddress(sizeAddr))
5202 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ARG, "adhocmatching invalid arg");
5203
5204 int* buflen = (int*)Memory::GetPointer(sizeAddr);
5205 SceNetAdhocMatchingMemberInfoEmu* buf2 = NULL;
5206 if (Memory::IsValidAddress(buf)) {
5207 buf2 = (SceNetAdhocMatchingMemberInfoEmu*)Memory::GetPointer(buf);
5208 }
5209
5210 // Number of Connected Peers, should we exclude timeout members?
5211 bool excludeTimedout = false; // false;
5212 uint32_t peercount = countConnectedPeers(context, excludeTimedout);
5213
5214 // Calculate Connected Peer Bytesize
5215 int available = sizeof(SceNetAdhocMatchingMemberInfoEmu) * peercount;
5216
5217 // Length Returner Mode
5218 if (buf == 0)
5219 {
5220 // Get Connected Peer Count
5221 *buflen = available;
5222 DEBUG_LOG(SCENET, "MemberList [Connected: %i]", peercount);
5223 }
5224
5225 // Normal Mode
5226 else
5227 {
5228 // Fix Negative Length
5229 if ((*buflen) < 0) *buflen = 0;
5230
5231 // Fix Oversize Request
5232 if ((*buflen) > available) *buflen = available;
5233
5234 // Clear Memory
5235 memset(buf2, 0, *buflen);
5236
5237 // Calculate Requested Peer Count
5238 int requestedpeers = (*buflen) / sizeof(SceNetAdhocMatchingMemberInfoEmu);
5239
5240 // Filled Request Counter
5241 int filledpeers = 0;
5242
5243 if (requestedpeers > 0)
5244 {
5245 // Add Self-Peer first, unless if there is existing Parent/P2P peer
5246 if (peercount == 1 || context->mode != PSP_ADHOC_MATCHING_MODE_CHILD) {
5247 // Add Local MAC
5248 buf2[filledpeers++].mac_addr = context->mac;
5249
5250 DEBUG_LOG(SCENET, "MemberSelf [%s]", mac2str(&context->mac).c_str());
5251 }
5252
5253 // Room for more than local peer
5254 if (requestedpeers > 1)
5255 {
5256 // P2P Mode
5257 if (context->mode == PSP_ADHOC_MATCHING_MODE_P2P)
5258 {
5259 // Find P2P Brother
5260 SceNetAdhocMatchingMemberInternal* p2p = findP2P(context, excludeTimedout);
5261
5262 // P2P Brother found
5263 if (p2p != NULL)
5264 {
5265 // Faking lastping
5266 auto friendpeer = findFriend(&p2p->mac);
5267 if (p2p->lastping != 0 && friendpeer != NULL && friendpeer->last_recv != 0)
5268 p2p->lastping = std::max(p2p->lastping, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta);
5269 else
5270 p2p->lastping = 0;
5271
5272 // Add P2P Brother MAC
5273 buf2[filledpeers++].mac_addr = p2p->mac;
5274
5275 DEBUG_LOG(SCENET, "MemberP2P [%s]", mac2str(&p2p->mac).c_str());
5276 }
5277 }
5278
5279 // Parent or Child Mode
5280 else
5281 {
5282 // Add Parent first
5283 SceNetAdhocMatchingMemberInternal* parentpeer = findParent(context);
5284 if (parentpeer != NULL) {
5285 // Faking lastping
5286 auto friendpeer = findFriend(&parentpeer->mac);
5287 if (parentpeer->lastping != 0 && friendpeer != NULL && friendpeer->last_recv != 0)
5288 parentpeer->lastping = std::max(parentpeer->lastping, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta);
5289 else
5290 parentpeer->lastping = 0;
5291
5292 // Add Parent MAC
5293 buf2[filledpeers++].mac_addr = parentpeer->mac;
5294
5295 DEBUG_LOG(SCENET, "MemberParent [%s]", mac2str(&parentpeer->mac).c_str());
5296 }
5297
5298 // We may need to rearrange children where last joined player placed last
5299 std::deque<SceNetAdhocMatchingMemberInternal*> sortedPeers;
5300
5301 // Iterate Peer List
5302 SceNetAdhocMatchingMemberInternal* peer = context->peerlist;
5303 for (; peer != NULL && filledpeers < requestedpeers; peer = peer->next)
5304 {
5305 // Should we exclude timedout members?
5306 if (!excludeTimedout || peer->lastping != 0) {
5307 // Faking lastping
5308 auto friendpeer = findFriend(&peer->mac);
5309 if (peer->lastping != 0 && friendpeer != NULL && friendpeer->last_recv != 0)
5310 peer->lastping = std::max(peer->lastping, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta);
5311 else
5312 peer->lastping = 0;
5313
5314 // Add Peer MAC
5315 sortedPeers.push_front(peer);
5316 }
5317 }
5318
5319 // Iterate rearranged peers
5320 for (const auto& peer : sortedPeers) {
5321 // Parent Mode
5322 if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT) {
5323 // Interested in Children
5324 if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) {
5325 // Add Child MAC
5326 buf2[filledpeers++].mac_addr = peer->mac;
5327
5328 DEBUG_LOG(SCENET, "MemberChild [%s]", mac2str(&peer->mac).c_str());
5329 }
5330 }
5331
5332 // Child Mode
5333 else {
5334 // Interested in Siblings
5335 if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) {
5336 // Add Peer MAC
5337 buf2[filledpeers++].mac_addr = peer->mac;
5338
5339 DEBUG_LOG(SCENET, "MemberSibling [%s]", mac2str(&peer->mac).c_str());
5340 }
5341 // Self Peer
5342 else if (peer->state == 0) {
5343 // Add Local MAC
5344 buf2[filledpeers++].mac_addr = peer->mac;
5345
5346 DEBUG_LOG(SCENET, "MemberSelf [%s]", mac2str(&peer->mac).c_str());
5347 }
5348
5349 }
5350 }
5351 sortedPeers.clear();
5352 }
5353 }
5354
5355 // Link Result List
5356 for (int i = 0; i < filledpeers - 1; i++)
5357 {
5358 // Link Next Element
5359 //buf2[i].next = &buf2[i + 1];
5360 buf2[i].next = buf + (sizeof(SceNetAdhocMatchingMemberInfoEmu) * (i + 1LL));
5361 }
5362 // Fix Last Element
5363 if (filledpeers > 0) buf2[filledpeers - 1].next = 0;
5364 }
5365
5366 // Fix Buffer Size
5367 *buflen = sizeof(SceNetAdhocMatchingMemberInfoEmu) * filledpeers;
5368 DEBUG_LOG(SCENET, "MemberList [Requested: %i][Discovered: %i]", requestedpeers, filledpeers);
5369 }
5370
5371 // Return Success
5372 return hleDelayResult(0, "delay 100 ~ 1000us", 100); // seems to have different thread running within the delay duration
5373 }
5374
5375 // Gran Turismo may replace the 1st bit of the 1st byte of MAC address's OUI with 0 (unicast bit), or replace the whole 6-bytes of MAC address with all 00 (invalid mac) for unknown reason
sceNetAdhocMatchingSendData(int matchingId,const char * mac,int dataLen,u32 dataAddr)5376 int sceNetAdhocMatchingSendData(int matchingId, const char *mac, int dataLen, u32 dataAddr) {
5377 WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingSendData(%i, %s, %i, %08x) at %08x", matchingId, mac2str((SceNetEtherAddr*)mac).c_str(), dataLen, dataAddr, currentMIPS->pc);
5378 if (!g_Config.bEnableWlan)
5379 return -1;
5380
5381 // Initialized Library
5382 if (netAdhocMatchingInited)
5383 {
5384 // Valid Arguments
5385 if (mac != NULL)
5386 {
5387 // Find Matching Context
5388 SceNetAdhocMatchingContext * context = findMatchingContext(matchingId);
5389
5390 // Found Context
5391 if (context != NULL)
5392 {
5393 // Running Context
5394 if (context->running)
5395 {
5396 // Invalid Data Length
5397 if (dataLen <=0 || dataAddr == 0)
5398 // Invalid Data Length
5399 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_DATALEN, "invalid datalen");
5400
5401 void* data = NULL;
5402 if (Memory::IsValidAddress(dataAddr)) data = Memory::GetPointer(dataAddr);
5403
5404 // Lock the peer
5405 std::lock_guard<std::recursive_mutex> peer_guard(peerlock);
5406
5407 // Find Target Peer
5408 SceNetAdhocMatchingMemberInternal* peer = findPeer(context, (SceNetEtherAddr*)mac);
5409
5410 // Found Peer
5411 if (peer != NULL)
5412 {
5413 // Valid Peer Connection State
5414 if (peer->state == PSP_ADHOC_MATCHING_PEER_PARENT || peer->state == PSP_ADHOC_MATCHING_PEER_CHILD || peer->state == PSP_ADHOC_MATCHING_PEER_P2P)
5415 {
5416 // Send in Progress
5417 if (peer->sending)
5418 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_DATA_BUSY, "data busy");
5419
5420 // Mark Peer as Sending
5421 peer->sending = 1;
5422
5423 // Send Data to Peer
5424 sendBulkDataPacket(context, &peer->mac, dataLen, data);
5425
5426 // Return Success
5427 return 0;
5428 }
5429
5430 // Not connected / accepted
5431 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_ESTABLISHED, "not established");
5432 }
5433
5434 // Peer not found
5435 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_UNKNOWN_TARGET, "unknown target");
5436 }
5437
5438 // Context not running
5439 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_RUNNING, "not running");
5440 }
5441
5442 // Invalid Matching ID
5443 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ID, "invalid id");
5444 }
5445
5446 // Invalid Arguments
5447 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ARG, "invalid arg");
5448 }
5449
5450 // Uninitialized Library
5451 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "not initialized");
5452 }
5453
sceNetAdhocMatchingAbortSendData(int matchingId,const char * mac)5454 int sceNetAdhocMatchingAbortSendData(int matchingId, const char *mac) {
5455 WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingAbortSendData(%i, %s)", matchingId, mac2str((SceNetEtherAddr*)mac).c_str());
5456 if (!g_Config.bEnableWlan)
5457 return -1;
5458
5459 // Initialized Library
5460 if (netAdhocMatchingInited)
5461 {
5462 // Valid Arguments
5463 if (mac != NULL)
5464 {
5465 // Find Matching Context
5466 SceNetAdhocMatchingContext * context = findMatchingContext(matchingId);
5467
5468 // Found Context
5469 if (context != NULL)
5470 {
5471 // Running Context
5472 if (context->running)
5473 {
5474 // Find Target Peer
5475 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, (SceNetEtherAddr *)mac);
5476
5477 // Found Peer
5478 if (peer != NULL)
5479 {
5480 // Peer is sending
5481 if (peer->sending)
5482 {
5483 // Set Peer as Bulk Idle
5484 peer->sending = 0;
5485
5486 // Stop Bulk Data Sending (if in progress)
5487 abortBulkTransfer(context, peer);
5488 }
5489
5490 // Return Success
5491 return 0;
5492 }
5493
5494 // Peer not found
5495 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_UNKNOWN_TARGET, "adhocmatching unknown target");
5496 }
5497
5498 // Context not running
5499 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_RUNNING, "adhocmatching not running");
5500 }
5501
5502 // Invalid Matching ID
5503 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ID, "adhocmatching invalid id");
5504 }
5505
5506 // Invalid Arguments
5507 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ARG, "adhocmatching invalid arg");
5508 }
5509
5510 // Uninitialized Library
5511 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhocmatching not initialized");
5512 }
5513
5514 // Get the maximum memory usage by the matching library
sceNetAdhocMatchingGetPoolMaxAlloc()5515 static int sceNetAdhocMatchingGetPoolMaxAlloc() {
5516 ERROR_LOG(SCENET, "UNIMPL sceNetAdhocMatchingGetPoolMaxAlloc() at %08x", currentMIPS->pc);
5517 if (!g_Config.bEnableWlan)
5518 return -1;
5519
5520 // Lazy way out - hardcoded return value
5521 return hleLogDebug(SCENET, fakePoolSize/2, "faked value");
5522 }
5523
sceNetAdhocMatchingGetPoolStat(u32 poolstatPtr)5524 int sceNetAdhocMatchingGetPoolStat(u32 poolstatPtr) {
5525 DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocMatchingGetPoolStat(%08x) at %08x", poolstatPtr, currentMIPS->pc);
5526 if (!g_Config.bEnableWlan)
5527 return -1;
5528
5529 // Initialized Library
5530 if (netAdhocMatchingInited)
5531 {
5532 SceNetMallocStat * poolstat = NULL;
5533 if (Memory::IsValidAddress(poolstatPtr)) poolstat = (SceNetMallocStat *)Memory::GetPointer(poolstatPtr);
5534
5535 // Valid Argument
5536 if (poolstat != NULL)
5537 {
5538 // Fill Poolstat with Fake Data
5539 poolstat->pool = fakePoolSize;
5540 poolstat->maximum = fakePoolSize / 2; // Max usage faked to halt the pool
5541 poolstat->free = fakePoolSize - poolstat->maximum;
5542
5543 // Return Success
5544 return 0;
5545 }
5546
5547 // Invalid Argument
5548 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ARG, "adhocmatching invalid arg");
5549 }
5550
5551 // Uninitialized Library
5552 return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhocmatching not initialized");
5553 }
5554
__NetTriggerCallbacks()5555 void __NetTriggerCallbacks()
5556 {
5557 std::lock_guard<std::recursive_mutex> adhocGuard(adhocEvtMtx);
5558 hleSkipDeadbeef();
5559 int delayus = adhocDefaultDelay;
5560
5561 auto params = adhocctlEvents.begin();
5562 if (params != adhocctlEvents.end())
5563 {
5564 int newState = adhocctlState;
5565 u32 flags = params->first;
5566 u32 error = params->second;
5567 u32_le args[3] = { 0, 0, 0 };
5568 args[0] = flags;
5569 args[1] = error;
5570 u64 now = (u64)(time_now_d() * 1000000.0);
5571
5572 // FIXME: When Joining a group, Do we need to wait for group creator's peer data before triggering the callback to make sure the game not to thinks we're the group creator?
5573 if ((flags != ADHOCCTL_EVENT_CONNECT && flags != ADHOCCTL_EVENT_GAME) || adhocConnectionType != ADHOC_JOIN || getActivePeerCount() > 0 || static_cast<s64>(now - adhocctlStartTime) > adhocDefaultTimeout)
5574 {
5575 // Since 0 is a valid index to types_ we use -1 to detects if it was loaded from an old save state
5576 if (actionAfterAdhocMipsCall < 0) {
5577 actionAfterAdhocMipsCall = __KernelRegisterActionType(AfterAdhocMipsCall::Create);
5578 }
5579
5580 delayus = adhocEventPollDelay; // May need to add an extra delay if a certain I/O Timing method causing disconnection issue
5581 switch (flags) {
5582 case ADHOCCTL_EVENT_CONNECT:
5583 newState = ADHOCCTL_STATE_CONNECTED;
5584 if (adhocConnectionType == ADHOC_CREATE)
5585 delayus = adhocEventDelay; // May affects Dissidia 012 and GTA VCS
5586 else if (adhocConnectionType == ADHOC_CONNECT)
5587 delayus = adhocEventDelay / 2;
5588 break;
5589 case ADHOCCTL_EVENT_SCAN: // notified only when scan completed?
5590 newState = ADHOCCTL_STATE_DISCONNECTED;
5591 //delayus = adhocEventDelay / 2;
5592 break;
5593 case ADHOCCTL_EVENT_DISCONNECT:
5594 newState = ADHOCCTL_STATE_DISCONNECTED;
5595 break;
5596 case ADHOCCTL_EVENT_GAME:
5597 {
5598 newState = ADHOCCTL_STATE_GAMEMODE;
5599 delayus = adhocEventDelay;
5600 // TODO: Use blocking PTP connection to sync the timing just like official prx did (which is done before notifying user-defined Adhocctl Handlers)
5601 // Workaround: Extra delay to prevent Joining player to progress faster than the Creator on Pocket Pool, but unbalanced delays could cause an issue on Shaun White Snowboarding :(
5602 if (adhocConnectionType == ADHOC_JOIN)
5603 delayus += adhocExtraDelay * 3;
5604 // Shows player list
5605 INFO_LOG(SCENET, "GameMode - All players have joined:");
5606 int i = 0;
5607 for (auto& mac : gameModeMacs) {
5608 INFO_LOG(SCENET, "GameMode macAddress#%d=%s", i++, mac2str(&mac).c_str());
5609 if (i >= ADHOCCTL_GAMEMODE_MAX_MEMBERS)
5610 break;
5611 }
5612 }
5613 break;
5614 case ADHOCCTL_EVENT_DISCOVER:
5615 newState = ADHOCCTL_STATE_DISCOVER;
5616 break;
5617 case ADHOCCTL_EVENT_WOL_INTERRUPT:
5618 newState = ADHOCCTL_STATE_WOL;
5619 break;
5620 case ADHOCCTL_EVENT_ERROR:
5621 delayus = adhocDefaultDelay * 3;
5622 break;
5623 }
5624
5625 for (std::map<int, AdhocctlHandler>::iterator it = adhocctlHandlers.begin(); it != adhocctlHandlers.end(); ++it) {
5626 DEBUG_LOG(SCENET, "AdhocctlCallback: [ID=%i][EVENT=%i][Error=%08x]", it->first, flags, error);
5627 args[2] = it->second.argument;
5628 AfterAdhocMipsCall* after = (AfterAdhocMipsCall*)__KernelCreateAction(actionAfterAdhocMipsCall);
5629 after->SetData(it->first, flags, args[2]);
5630 hleEnqueueCall(it->second.entryPoint, 3, args, after);
5631 }
5632 adhocctlEvents.pop_front();
5633 // Since we don't have beforeAction, simulate it using ScheduleEvent
5634 ScheduleAdhocctlState(flags, newState, delayus, "adhocctl callback state");
5635 return;
5636 }
5637 }
5638
5639 // Must be delayed long enough whenever there is a pending callback. Should it be 100-500ms for Adhocctl Events? or Not Less than the delays on sceNetAdhocctl HLE?
5640 sceKernelDelayThread(adhocDefaultDelay);
5641 }
5642
__NetMatchingCallbacks()5643 void __NetMatchingCallbacks() //(int matchingId)
5644 {
5645 std::lock_guard<std::recursive_mutex> adhocGuard(adhocEvtMtx);
5646 hleSkipDeadbeef();
5647 int delayus = adhocDefaultDelay;
5648
5649 auto params = matchingEvents.begin();
5650 if (params != matchingEvents.end()) {
5651 u32_le *args = params->data;
5652 //auto context = findMatchingContext(args[0]);
5653
5654 if (actionAfterMatchingMipsCall < 0) {
5655 actionAfterMatchingMipsCall = __KernelRegisterActionType(AfterMatchingMipsCall::Create);
5656 }
5657 DEBUG_LOG(SCENET, "AdhocMatching - Remaining Events: %zu", matchingEvents.size());
5658 DEBUG_LOG(SCENET, "AdhocMatchingCallback: [ID=%i][EVENT=%i][%s]", args[0], args[1], mac2str((SceNetEtherAddr *)Memory::GetPointer(args[2])).c_str());
5659 AfterMatchingMipsCall *after = (AfterMatchingMipsCall *)__KernelCreateAction(actionAfterMatchingMipsCall);
5660 after->SetData(args[0], args[1], args[2]);
5661 hleEnqueueCall(args[5], 5, args, after);
5662 matchingEvents.pop_front();
5663 delayus = adhocMatchingEventDelay; // Add extra delay to prevent I/O Timing method from causing disconnection, but delaying too long may cause matchingEvents to pile up
5664 }
5665
5666 // Must be delayed long enough whenever there is a pending callback. Should it be 10-100ms for Matching Events? or Not Less than the delays on sceNetAdhocMatching HLE?
5667 sceKernelDelayThread(delayus);
5668 }
5669
5670 const HLEFunction sceNetAdhoc[] = {
5671 {0XE1D621D7, &WrapU_V<sceNetAdhocInit>, "sceNetAdhocInit", 'x', "" },
5672 {0XA62C6F57, &WrapI_V<sceNetAdhocTerm>, "sceNetAdhocTerm", 'i', "" },
5673 {0X0AD043ED, &WrapI_C<sceNetAdhocctlConnect>, "sceNetAdhocctlConnect", 'i', "s" },
5674 {0X6F92741B, &WrapI_CIIU<sceNetAdhocPdpCreate>, "sceNetAdhocPdpCreate", 'i', "siix" },
5675 {0XABED3790, &WrapI_ICUVIII<sceNetAdhocPdpSend>, "sceNetAdhocPdpSend", 'i', "isxpiii" },
5676 {0XDFE53E03, &WrapI_IVVVVUI<sceNetAdhocPdpRecv>, "sceNetAdhocPdpRecv", 'i', "ippppxi" },
5677 {0X7F27BB5E, &WrapI_II<sceNetAdhocPdpDelete>, "sceNetAdhocPdpDelete", 'i', "ii" },
5678 {0XC7C1FC57, &WrapI_UU<sceNetAdhocGetPdpStat>, "sceNetAdhocGetPdpStat", 'i', "xx" },
5679 {0X157E6225, &WrapI_II<sceNetAdhocPtpClose>, "sceNetAdhocPtpClose", 'i', "ii" },
5680 {0X4DA4C788, &WrapI_IUUII<sceNetAdhocPtpSend>, "sceNetAdhocPtpSend", 'i', "ixxii" },
5681 {0X877F6D66, &WrapI_CICIIIII<sceNetAdhocPtpOpen>, "sceNetAdhocPtpOpen", 'i', "sisiiiii" },
5682 {0X8BEA2B3E, &WrapI_IUUII<sceNetAdhocPtpRecv>, "sceNetAdhocPtpRecv", 'i', "ixxii" },
5683 {0X9DF81198, &WrapI_IUUII<sceNetAdhocPtpAccept>, "sceNetAdhocPtpAccept", 'i', "ixxii" },
5684 {0XE08BDAC1, &WrapI_CIIIIII<sceNetAdhocPtpListen>, "sceNetAdhocPtpListen", 'i', "siiiiii" },
5685 {0XFC6FC07B, &WrapI_III<sceNetAdhocPtpConnect>, "sceNetAdhocPtpConnect", 'i', "iii" },
5686 {0X9AC2EEAC, &WrapI_III<sceNetAdhocPtpFlush>, "sceNetAdhocPtpFlush", 'i', "iii" },
5687 {0XB9685118, &WrapI_UU<sceNetAdhocGetPtpStat>, "sceNetAdhocGetPtpStat", 'i', "xx" },
5688 {0X3278AB0C, &WrapI_CUI<sceNetAdhocGameModeCreateReplica>, "sceNetAdhocGameModeCreateReplica", 'i', "sxi" },
5689 {0X98C204C8, &WrapI_V<sceNetAdhocGameModeUpdateMaster>, "sceNetAdhocGameModeUpdateMaster", 'i', "" },
5690 {0XFA324B4E, &WrapI_IU<sceNetAdhocGameModeUpdateReplica>, "sceNetAdhocGameModeUpdateReplica", 'i', "ix" },
5691 {0XA0229362, &WrapI_V<sceNetAdhocGameModeDeleteMaster>, "sceNetAdhocGameModeDeleteMaster", 'i', "" },
5692 {0X0B2228E9, &WrapI_I<sceNetAdhocGameModeDeleteReplica>, "sceNetAdhocGameModeDeleteReplica", 'i', "i" },
5693 {0X7F75C338, &WrapI_UI<sceNetAdhocGameModeCreateMaster>, "sceNetAdhocGameModeCreateMaster", 'i', "xi" },
5694 {0X73BFD52D, &WrapI_II<sceNetAdhocSetSocketAlert>, "sceNetAdhocSetSocketAlert", 'i', "ii" },
5695 {0X4D2CE199, &WrapI_IU<sceNetAdhocGetSocketAlert>, "sceNetAdhocGetSocketAlert", 'i', "ix" },
5696 {0X7A662D6B, &WrapI_UIII<sceNetAdhocPollSocket>, "sceNetAdhocPollSocket", 'i', "xiii" },
5697 // Fake function for PPSSPP's use.
5698 {0X756E6E6F, &WrapV_V<__NetTriggerCallbacks>, "__NetTriggerCallbacks", 'v', "" },
5699 };
5700
5701 const HLEFunction sceNetAdhocMatching[] = {
5702 {0X2A2A1E07, &WrapI_U<sceNetAdhocMatchingInit>, "sceNetAdhocMatchingInit", 'i', "x" },
5703 {0X7945ECDA, &WrapI_V<sceNetAdhocMatchingTerm>, "sceNetAdhocMatchingTerm", 'i', "" },
5704 {0XCA5EDA6F, &WrapI_IIIIIIIIU<sceNetAdhocMatchingCreate>, "sceNetAdhocMatchingCreate", 'i', "iiiiiiiix"},
5705 {0X93EF3843, &WrapI_IIIIIIU<sceNetAdhocMatchingStart>, "sceNetAdhocMatchingStart", 'i', "iiiiiix" },
5706 {0xE8454C65, &WrapI_IIIIIIIIU<sceNetAdhocMatchingStart2>, "sceNetAdhocMatchingStart2", 'i', "iiiiiiiix"},
5707 {0X32B156B3, &WrapI_I<sceNetAdhocMatchingStop>, "sceNetAdhocMatchingStop", 'i', "i" },
5708 {0XF16EAF4F, &WrapI_I<sceNetAdhocMatchingDelete>, "sceNetAdhocMatchingDelete", 'i', "i" },
5709 {0X5E3D4B79, &WrapI_ICIU<sceNetAdhocMatchingSelectTarget>, "sceNetAdhocMatchingSelectTarget", 'i', "isix" },
5710 {0XEA3C6108, &WrapI_IC<sceNetAdhocMatchingCancelTarget>, "sceNetAdhocMatchingCancelTarget", 'i', "is" },
5711 {0X8F58BEDF, &WrapI_ICIU<sceNetAdhocMatchingCancelTargetWithOpt>, "sceNetAdhocMatchingCancelTargetWithOpt", 'i', "isix" },
5712 {0XB5D96C2A, &WrapI_IUU<sceNetAdhocMatchingGetHelloOpt>, "sceNetAdhocMatchingGetHelloOpt", 'i', "ixx" },
5713 {0XB58E61B7, &WrapI_IIU<sceNetAdhocMatchingSetHelloOpt>, "sceNetAdhocMatchingSetHelloOpt", 'i', "iix" },
5714 {0XC58BCD9E, &WrapI_IUU<sceNetAdhocMatchingGetMembers>, "sceNetAdhocMatchingGetMembers", 'i', "ixx" },
5715 {0XF79472D7, &WrapI_ICIU<sceNetAdhocMatchingSendData>, "sceNetAdhocMatchingSendData", 'i', "isix" },
5716 {0XEC19337D, &WrapI_IC<sceNetAdhocMatchingAbortSendData>, "sceNetAdhocMatchingAbortSendData", 'i', "is" },
5717 {0X40F8F435, &WrapI_V<sceNetAdhocMatchingGetPoolMaxAlloc>, "sceNetAdhocMatchingGetPoolMaxAlloc", 'i', "" },
5718 {0X9C5CFB7D, &WrapI_U<sceNetAdhocMatchingGetPoolStat>, "sceNetAdhocMatchingGetPoolStat", 'i', "x" },
5719 // Fake function for PPSSPP's use.
5720 {0X756E6F00, &WrapV_V<__NetMatchingCallbacks>, "__NetMatchingCallbacks", 'v', "" },
5721 };
5722
NetAdhocctl_ExitGameMode()5723 int NetAdhocctl_ExitGameMode() {
5724 if (gameModeSocket > 0) {
5725 NetAdhocPdp_Delete(gameModeSocket, 0);
5726 gameModeSocket = (int)INVALID_SOCKET;
5727 }
5728
5729 deleteAllGMB();
5730 gameModePeerPorts.clear();
5731
5732 adhocctlCurrentMode = ADHOCCTL_MODE_NONE;
5733 netAdhocGameModeEntered = false;
5734 return NetAdhocctl_Disconnect();
5735 }
5736
sceNetAdhocctlExitGameMode()5737 static int sceNetAdhocctlExitGameMode() {
5738 WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlExitGameMode() at %08x", currentMIPS->pc);
5739
5740 return NetAdhocctl_ExitGameMode();
5741 }
5742
sceNetAdhocctlGetGameModeInfo(u32 infoAddr)5743 static int sceNetAdhocctlGetGameModeInfo(u32 infoAddr) {
5744 DEBUG_LOG(SCENET, "sceNetAdhocctlGetGameModeInfo(%08x)", infoAddr);
5745 if (!netAdhocctlInited)
5746 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized");
5747
5748 if (!Memory::IsValidAddress(infoAddr))
5749 return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg");
5750
5751 SceNetAdhocctlGameModeInfo* gmInfo = (SceNetAdhocctlGameModeInfo*)Memory::GetPointer(infoAddr);
5752 // Writes number of participants and each participating MAC address into infoAddr/gmInfo
5753 gmInfo->num = static_cast<s32_le>(gameModeMacs.size());
5754 int i = 0;
5755 for (auto& mac : gameModeMacs) {
5756 VERBOSE_LOG(SCENET, "GameMode macAddress#%d=%s", i, mac2str(&mac).c_str());
5757 gmInfo->members[i++] = mac;
5758 if (i >= ADHOCCTL_GAMEMODE_MAX_MEMBERS)
5759 break;
5760 }
5761
5762 hleEatMicro(100);
5763 return 0;
5764 }
5765
sceNetAdhocctlGetPeerList(u32 sizeAddr,u32 bufAddr)5766 static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) {
5767 s32_le *buflen = NULL;
5768 if (Memory::IsValidAddress(sizeAddr)) buflen = (s32_le *)Memory::GetPointer(sizeAddr);
5769 SceNetAdhocctlPeerInfoEmu *buf = NULL;
5770 if (Memory::IsValidAddress(bufAddr)) buf = (SceNetAdhocctlPeerInfoEmu *)Memory::GetPointer(bufAddr);
5771
5772 DEBUG_LOG(SCENET, "sceNetAdhocctlGetPeerList([%08x]=%i, %08x) at %08x", sizeAddr, /*buflen ? *buflen : -1*/Memory::Read_U32(sizeAddr), bufAddr, currentMIPS->pc);
5773 if (!g_Config.bEnableWlan) {
5774 return -1;
5775 }
5776
5777 // Initialized Library
5778 if (netAdhocctlInited) {
5779 // Minimum Arguments
5780 if (buflen != NULL) {
5781 // FIXME: Sometimes returing 0x80410682 when Adhocctl is still BUSY or before AdhocctlGetState became ADHOCCTL_STATE_CONNECTED or related to Auth/Library ?
5782
5783 // Multithreading Lock
5784 peerlock.lock();
5785
5786 bool excludeTimedout = true;
5787 // Length Calculation Mode
5788 if (buf == NULL) {
5789 int activePeers = getActivePeerCount(excludeTimedout);
5790 *buflen = activePeers * sizeof(SceNetAdhocctlPeerInfoEmu);
5791 DEBUG_LOG(SCENET, "PeerList [Active: %i]", activePeers);
5792 }
5793 // Normal Mode
5794 else {
5795 // Discovery Counter
5796 int discovered = 0;
5797
5798 // Calculate Request Count
5799 int requestcount = *buflen / sizeof(SceNetAdhocctlPeerInfoEmu);
5800
5801 // FIXME: When bufAddr is not null but buffer size is smaller than activePeers * sizeof(SceNetAdhocctlPeerInfoEmu), simply return buffer size = 0 without filling the buffer?
5802
5803 // Clear Memory
5804 memset(buf, 0, *buflen);
5805
5806 // Minimum Arguments
5807 if (requestcount > 0) {
5808 // Peer Reference
5809 SceNetAdhocctlPeerInfo * peer = friends;
5810
5811 // Iterate Peers
5812 for (; peer != NULL && discovered < requestcount; peer = peer->next) {
5813 // Exclude Soon to be timedout peers?
5814 if (!excludeTimedout || peer->last_recv != 0) {
5815 // Faking Last Receive Time
5816 if (peer->last_recv != 0)
5817 peer->last_recv = std::max(peer->last_recv, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta);
5818
5819 // Copy Peer Info
5820 buf[discovered].nickname = peer->nickname;
5821 buf[discovered].mac_addr = peer->mac_addr;
5822 buf[discovered].flags = 0x0400;
5823 buf[discovered].last_recv = peer->last_recv;
5824 discovered++;
5825
5826 u32_le ipaddr = peer->ip_addr;
5827 DEBUG_LOG(SCENET, "Peer [%s][%s][%s][%llu]", mac2str(&peer->mac_addr).c_str(), ip2str(*(in_addr*)&ipaddr).c_str(), (const char*)&peer->nickname.data, peer->last_recv);
5828 }
5829 }
5830
5831 // Link List
5832 for (int i = 0; i < discovered - 1; i++) {
5833 // Link Network
5834 buf[i].next = bufAddr+(sizeof(SceNetAdhocctlPeerInfoEmu)*i) + sizeof(SceNetAdhocctlPeerInfoEmu);
5835 }
5836 // Fix Last Element
5837 if (discovered > 0) buf[discovered - 1].next = 0;
5838 }
5839
5840 // Fix Size
5841 *buflen = discovered * sizeof(SceNetAdhocctlPeerInfoEmu);
5842 DEBUG_LOG(SCENET, "PeerList [Requested: %i][Discovered: %i]", requestcount, discovered);
5843 }
5844
5845 // Multithreading Unlock
5846 peerlock.unlock();
5847
5848 // Return Success
5849 return hleDelayResult(0, "delay 100 ~ 1000us", 100); // seems to have different thread running within the delay duration
5850 }
5851
5852 // Invalid Arguments
5853 return hleLogDebug(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg");
5854 }
5855
5856 // Uninitialized Library
5857 return hleLogDebug(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized");
5858 }
5859
sceNetAdhocctlGetAddrByName(const char * nickName,u32 sizeAddr,u32 bufAddr)5860 static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 bufAddr) {
5861 s32_le *buflen = NULL; //int32_t
5862 if (Memory::IsValidAddress(sizeAddr)) buflen = (s32_le *)Memory::GetPointer(sizeAddr);
5863
5864 char nckName[ADHOCCTL_NICKNAME_LEN];
5865 memcpy(nckName, nickName, ADHOCCTL_NICKNAME_LEN); // Copied to null-terminated var to prevent unexpected behaviour on Logs
5866 nckName[ADHOCCTL_NICKNAME_LEN - 1] = 0;
5867
5868 WARN_LOG_REPORT_ONCE(sceNetAdhocctlGetAddrByName, SCENET, "UNTESTED sceNetAdhocctlGetAddrByName(%s, [%08x]=%d/%zu, %08x) at %08x", nckName, sizeAddr, buflen ? *buflen : -1, sizeof(SceNetAdhocctlPeerInfoEmu), bufAddr, currentMIPS->pc);
5869
5870 // Library initialized
5871 if (netAdhocctlInited)
5872 {
5873 // Valid Arguments
5874 if (nickName != NULL && buflen != NULL)
5875 {
5876 SceNetAdhocctlPeerInfoEmu *buf = NULL;
5877 if (Memory::IsValidAddress(bufAddr)) buf = (SceNetAdhocctlPeerInfoEmu *)Memory::GetPointer(bufAddr);
5878
5879 // Multithreading Lock
5880 peerlock.lock();
5881
5882 // Length Calculation Mode
5883 if (buf == NULL) {
5884 int foundName = getNicknameCount(nickName);
5885 *buflen = foundName * sizeof(SceNetAdhocctlPeerInfoEmu);
5886 DEBUG_LOG(SCENET, "PeerNameList [%s: %i]", nickName, foundName);
5887 }
5888 // Normal Information Mode
5889 else
5890 {
5891 // Clear Memory
5892 memset(buf, 0, *buflen);
5893
5894 // Discovered Player Count
5895 int discovered = 0;
5896
5897 // Calculate Requested Elements
5898 int requestcount = *buflen / sizeof(SceNetAdhocctlPeerInfoEmu);
5899
5900 // Minimum Space available
5901 if (requestcount > 0)
5902 {
5903 // Local Nickname Matches
5904 if (strncmp((char *)¶meter.nickname.data, nickName, ADHOCCTL_NICKNAME_LEN) == 0)
5905 {
5906 // Get Local IP Address
5907 sockaddr_in addr;
5908 SceNetEtherAddr mac;
5909
5910 getLocalIp(&addr);
5911 buf[discovered].nickname = parameter.nickname;
5912 buf[discovered].nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char
5913 getLocalMac(&mac);
5914 buf[discovered].mac_addr = mac;
5915 buf[discovered].flags = 0x0400;
5916 u64 lastrecv = std::max(0LL, static_cast<s64>(CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta));
5917 buf[discovered++].last_recv = lastrecv;
5918
5919 DEBUG_LOG(SCENET, "Peer [%s][%s][%s][%llu]", mac2str(&mac).c_str(), ip2str(addr.sin_addr).c_str(), nickName, lastrecv);
5920 }
5921
5922 // Peer Reference
5923 SceNetAdhocctlPeerInfo * peer = friends;
5924
5925 // Iterate Peers
5926 for (; peer != NULL && discovered < requestcount; peer = peer->next)
5927 {
5928 // Match found
5929 if (peer->last_recv != 0 && strncmp((char *)&peer->nickname.data, nickName, ADHOCCTL_NICKNAME_LEN) == 0)
5930 {
5931 // Fake Receive Time
5932 peer->last_recv = std::max(peer->last_recv, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta);
5933
5934 // Copy Peer Info
5935 buf[discovered].nickname = peer->nickname;
5936 buf[discovered].nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char
5937 buf[discovered].mac_addr = peer->mac_addr;
5938 buf[discovered].flags = 0x0400;
5939 buf[discovered++].last_recv = peer->last_recv;
5940
5941 u32_le ipaddr = peer->ip_addr;
5942 DEBUG_LOG(SCENET, "Peer [%s][%s][%s][%llu]", mac2str(&peer->mac_addr).c_str(), ip2str(*(in_addr*)&ipaddr).c_str(), (const char*)&peer->nickname.data, peer->last_recv);
5943 }
5944 }
5945
5946 // Link List
5947 for (int i = 0; i < discovered - 1; i++)
5948 {
5949 // Link Network
5950 buf[i].next = bufAddr + (sizeof(SceNetAdhocctlPeerInfoEmu)*i) + sizeof(SceNetAdhocctlPeerInfoEmu);
5951 }
5952
5953 // Fix Last Element
5954 if (discovered > 0) buf[discovered - 1].next = 0;
5955 }
5956
5957 // Fix Buffer Size
5958 *buflen = discovered * sizeof(SceNetAdhocctlPeerInfoEmu);
5959 DEBUG_LOG(SCENET, "PeerNameList [%s][Requested: %i][Discovered: %i]", nickName, requestcount, discovered);
5960 }
5961
5962 // Multithreading Unlock
5963 peerlock.unlock();
5964
5965 // Return Success
5966 return hleLogDebug(SCENET, hleDelayResult(0, "delay 100 ~ 1000us", 100), "success"); // FIXME: Might have similar delay with GetPeerList? need to know which games using this tho
5967 }
5968
5969 // Invalid Arguments
5970 return ERROR_NET_ADHOCCTL_INVALID_ARG;
5971 }
5972
5973 // Library uninitialized
5974 return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
5975 }
5976
5977 const HLEFunction sceNetAdhocctl[] = {
5978 {0XE26F226E, &WrapU_IIU<sceNetAdhocctlInit>, "sceNetAdhocctlInit", 'x', "iix" },
5979 {0X9D689E13, &WrapI_V<sceNetAdhocctlTerm>, "sceNetAdhocctlTerm", 'i', "" },
5980 {0X20B317A0, &WrapU_UU<sceNetAdhocctlAddHandler>, "sceNetAdhocctlAddHandler", 'x', "xx" },
5981 {0X6402490B, &WrapU_U<sceNetAdhocctlDelHandler>, "sceNetAdhocctlDelHandler", 'x', "x" },
5982 {0X34401D65, &WrapU_V<sceNetAdhocctlDisconnect>, "sceNetAdhocctlDisconnect", 'x', "" },
5983 {0X0AD043ED, &WrapI_C<sceNetAdhocctlConnect>, "sceNetAdhocctlConnect", 'i', "s" },
5984 {0X08FFF7A0, &WrapI_V<sceNetAdhocctlScan>, "sceNetAdhocctlScan", 'i', "" },
5985 {0X75ECD386, &WrapI_U<sceNetAdhocctlGetState>, "sceNetAdhocctlGetState", 'i', "x" },
5986 {0X8916C003, &WrapI_CU<sceNetAdhocctlGetNameByAddr>, "sceNetAdhocctlGetNameByAddr", 'i', "sx" },
5987 {0XDED9D28E, &WrapI_U<sceNetAdhocctlGetParameter>, "sceNetAdhocctlGetParameter", 'i', "x" },
5988 {0X81AEE1BE, &WrapI_UU<sceNetAdhocctlGetScanInfo>, "sceNetAdhocctlGetScanInfo", 'i', "xx" },
5989 {0X5E7F79C9, &WrapI_U<sceNetAdhocctlJoin>, "sceNetAdhocctlJoin", 'i', "x" },
5990 {0X8DB83FDC, &WrapI_CIU<sceNetAdhocctlGetPeerInfo>, "sceNetAdhocctlGetPeerInfo", 'i', "six" },
5991 {0XEC0635C1, &WrapI_C<sceNetAdhocctlCreate>, "sceNetAdhocctlCreate", 'i', "s" },
5992 {0XA5C055CE, &WrapI_CIIUII<sceNetAdhocctlCreateEnterGameMode>, "sceNetAdhocctlCreateEnterGameMode", 'i', "siixii" },
5993 {0X1FF89745, &WrapI_CCII<sceNetAdhocctlJoinEnterGameMode>, "sceNetAdhocctlJoinEnterGameMode", 'i', "ssii" },
5994 {0XCF8E084D, &WrapI_V<sceNetAdhocctlExitGameMode>, "sceNetAdhocctlExitGameMode", 'i', "" },
5995 {0XE162CB14, &WrapI_UU<sceNetAdhocctlGetPeerList>, "sceNetAdhocctlGetPeerList", 'i', "xx" },
5996 {0X362CBE8F, &WrapI_U<sceNetAdhocctlGetAdhocId>, "sceNetAdhocctlGetAdhocId", 'i', "x" },
5997 {0X5A014CE0, &WrapI_U<sceNetAdhocctlGetGameModeInfo>, "sceNetAdhocctlGetGameModeInfo", 'i', "x" },
5998 {0X99560ABE, &WrapI_CUU<sceNetAdhocctlGetAddrByName>, "sceNetAdhocctlGetAddrByName", 'i', "sxx" },
5999 {0XB0B80E80, &WrapI_CIIIUUI<sceNetAdhocctlCreateEnterGameModeMin>, "sceNetAdhocctlCreateEnterGameModeMin", 'i', "siiixxi" }, // ??
6000 };
6001
6002 // Return value: 0/0x80410005/0x80411301/error returned from sceNetAdhocctl_lib_F8BABD85[/error returned from sceUtilityGetSystemParamInt?]
sceNetAdhocDiscoverInitStart(u32 paramAddr)6003 int sceNetAdhocDiscoverInitStart(u32 paramAddr) {
6004 WARN_LOG_REPORT_ONCE(sceNetAdhocDiscoverInitStart, SCENET, "UNIMPL sceNetAdhocDiscoverInitStart(%08x) at %08x", paramAddr, currentMIPS->pc);
6005 // FIXME: Most AdhocDiscover syscalls will return 0x80410005 if (sceKernelCheckThreadStack_user() < 0x00000FE0), AdhocDiscover seems to be storing some data in the stack, while we use global variables for these
6006 if (sceKernelCheckThreadStack() < 0x00000FE0)
6007 return 0x80410005;
6008 // TODO: Allocate internal buffer/struct (on the stack?) to be returned on sceNetAdhocDiscoverUpdate (the struct may contains WLAN channel from sceUtilityGetSystemParamInt at offset 0xA0 ?), setup adhocctl state callback handler to detects state change (using sceNetAdhocctl_lib_F8BABD85(stateCallbackFunction=0x09F436F8, adhocctlStateCallbackArg=0x0) on JPCSP+prx)
6009 u32 bufSize = 256; // dummy size, not sure how large it supposed to be, may be at least 0x3c bytes like in param->unknown2 ?
6010 if (netAdhocDiscoverBufAddr == 0) {
6011 netAdhocDiscoverBufAddr = userMemory.Alloc(bufSize, true, "AdhocDiscover"); // The address returned on DiscoverUpdate seems to be much higher than the param address, closer to the internal stateCallbackFunction address
6012 if (!Memory::IsValidAddress(netAdhocDiscoverBufAddr))
6013 return 0x80410005;
6014 Memory::Memset(netAdhocDiscoverBufAddr, 0, bufSize);
6015 }
6016 // FIME: Not sure what is this address 0x000010B0 used for (current Step may be?), but return 0x80411301 if (*((int *) 0x000010B0) != 0)
6017 //if (Memory::Read_U32(netAdhocDiscoverBufAddr + 0x80) != 0) //if (*((int*)Memory::GetPointer(0x000010B0)) != 0)
6018 // return 0x80411301; // Already Initialized/Started?
6019 // TODO: Need to findout whether using invalid params or param address will return an error code or not
6020 netAdhocDiscoverParam = (SceNetAdhocDiscoverParam*)Memory::GetPointer(paramAddr);
6021 if (!netAdhocDiscoverParam)
6022 return hleLogError(SCENET, -1, "invalid param?");
6023 // FIXME: paramAddr seems to be stored at 0x000010D8 without validating the value first
6024 //*((int*)Memory::GetPointer(0x000010D8)) = paramAddr;
6025
6026 // Based on Legend Of The Dragon:
6027 // The 1st 24 x 32bit(addr) seems to be pointers to subroutine containings sceNetAdhocctlCreate/sceNetAdhocctlJoin/sceNetAdhocctlDisconnect/sceNetAdhocctlScan/sceNetRand/sceKernelGetSystemTimeWide/etc.
6028 // Offset 0x60: 10 00 06 06
6029 // Offset 0x70: FF FF FF FF (before Init) -> 00 00 00 00 (after Init) -> FF FF FF FF (after Term) // Seems to be value returned from sceNetAdhocctl_lib_F8BABD85, and the address (0x000010A0) seems to be the lowest one for storing data
6030 // Offset 0x80: 00 -> 0B/0C/0D/13 -> 00 // This seems to be (current step?) at address at 0x000010B0 (ie. *((int *) 0x000010B0) = 0x0000000B), something todo with param->unknown1(sleep mode?)
6031 // Offset 0x84: 00 -> 03 -> 03 // somekind of State? Something todo with param->unknown1(sleep mode?) along with data at 0x000010B0 (current step?)
6032 // Offset 0x98: 0000 -> 0000/0200 -> 0000 // This seems to be somekind of flags at 0x000010C8 (ie. *((int *) 0x000010C8) = (var4 | 0x00000080)), something todo with data at 0x000010B0 (current step?)
6033 // Offset 0xA0: WLAN channel from sceUtilityGetSystemParamInt (ie. sceUtilityGetSystemParamInt(0x00000002, 0x000010D0) on decompiled prx, but on JPCSP+prx Logs it's sceUtilityGetSystemParamInt(0x00000002, 0x09F43FD0))
6034 // Offset 0xA4: Seems to be at 0x000010D4 and related to RequestSuspend
6035 // Offset 0xA8: paramAddr // This seems to be a fixed address at 0x000010D8 (ie. *((int *) 0x000010D8) = paramAddr)
6036 // The rest are zeroed
6037 Memory::Write_U32(0x06060010, netAdhocDiscoverBufAddr + 0x60);
6038 Memory::Write_U32(0xffffffff, netAdhocDiscoverBufAddr + 0x70);
6039 if (netAdhocDiscoverParam->unknown1 == 0) {
6040 Memory::Write_U32(0x0B, netAdhocDiscoverBufAddr + 0x80);
6041 Memory::Write_U32(0x03, netAdhocDiscoverBufAddr + 0x84);
6042 }
6043 else if (netAdhocDiscoverParam->unknown1 == 1) {
6044 Memory::Write_U32(0x0F, netAdhocDiscoverBufAddr + 0x80);
6045 Memory::Write_U32(0x04, netAdhocDiscoverBufAddr + 0x84);
6046 }
6047 Memory::Write_U32(0, netAdhocDiscoverBufAddr + 0x98);
6048 Memory::Write_U32(g_Config.iWlanAdhocChannel, netAdhocDiscoverBufAddr + 0xA0);
6049 Memory::Write_U32(0, netAdhocDiscoverBufAddr + 0xA4);
6050 Memory::Write_U32(paramAddr, netAdhocDiscoverBufAddr + 0xA8);
6051
6052 char grpName[ADHOCCTL_GROUPNAME_LEN + 1] = { 0 };
6053 if (netAdhocDiscoverParam->groupName)
6054 memcpy(grpName, netAdhocDiscoverParam->groupName, ADHOCCTL_GROUPNAME_LEN); // For logging purpose, must not be truncated
6055 DEBUG_LOG(SCENET, "sceNetAdhocDiscoverInitStart - Param.Unknown1 : %08x", netAdhocDiscoverParam->unknown1);
6056 DEBUG_LOG(SCENET, "sceNetAdhocDiscoverInitStart - Param.GroupName: [%s]", grpName);
6057 DEBUG_LOG(SCENET, "sceNetAdhocDiscoverInitStart - Param.Unknown2 : %08x", netAdhocDiscoverParam->unknown2);
6058 DEBUG_LOG(SCENET, "sceNetAdhocDiscoverInitStart - Param.Result : %08x", netAdhocDiscoverParam->result);
6059
6060 // TODO: Check whether we're already in the correct group and change the status and result accordingly
6061 netAdhocDiscoverIsStopping = false;
6062 netAdhocDiscoverStatus = NET_ADHOC_DISCOVER_STATUS_IN_PROGRESS;
6063 netAdhocDiscoverParam->result = NET_ADHOC_DISCOVER_RESULT_NO_PEER_FOUND;
6064 netAdhocDiscoverStartTime = CoreTiming::GetGlobalTimeUsScaled();
6065 return hleLogSuccessInfoX(SCENET, 0);
6066 }
6067
6068 // Note1: When canceling the progress, Legend Of The Dragon will use DiscoverStop -> AdhocctlDisconnect -> DiscoverTerm (when status changed to 2)
6069 // Note2: When result = NO_PEER_FOUND or PEER_FOUND the progress can no longer be canceled on Legend Of The Dragon
sceNetAdhocDiscoverStop()6070 int sceNetAdhocDiscoverStop() {
6071 WARN_LOG(SCENET, "UNIMPL sceNetAdhocDiscoverStop()");
6072 if (sceKernelCheckThreadStack() < 0x00000FF0)
6073 return 0x80410005;
6074
6075 if (Memory::Read_U32(netAdhocDiscoverBufAddr + 0x80) > 0 && (Memory::Read_U32(netAdhocDiscoverBufAddr + 0x80)^0x13) > 0) {
6076 Memory::Write_U32(Memory::Read_U32(netAdhocDiscoverBufAddr + 0x98) | 0x20, netAdhocDiscoverBufAddr + 0x98);
6077 Memory::Write_U32(0, netAdhocDiscoverBufAddr + 0xA4);
6078 }
6079 // FIXME: Doesn't seems to be immediately changed the status, may be waiting until Disconnected from Adhocctl before changing the status to Completed?
6080 netAdhocDiscoverIsStopping = true;
6081 //netAdhocDiscoverStatus = NET_ADHOC_DISCOVER_STATUS_COMPLETED;
6082 //if (netAdhocDiscoverParam) netAdhocDiscoverParam->result = NET_ADHOC_DISCOVER_RESULT_CANCELED;
6083 return 0;
6084 }
6085
sceNetAdhocDiscoverTerm()6086 int sceNetAdhocDiscoverTerm() {
6087 WARN_LOG(SCENET, "UNIMPL sceNetAdhocDiscoverTerm() at %08x", currentMIPS->pc);
6088 /*
6089 if (sceKernelCheckThreadStack() < 0x00000FF0)
6090 return 0x80410005;
6091
6092 if (!(Memory::Read_U32(netAdhocDiscoverBufAddr + 0x80) > 0 && (Memory::Read_U32(netAdhocDiscoverBufAddr + 0x80) ^ 0x13) > 0))
6093 return 0x80411301; // Not Initialized/Started yet?
6094 */
6095 // TODO: Use sceNetAdhocctl_lib_1C679240 to remove adhocctl state callback handler setup in sceNetAdhocDiscoverInitStart
6096 /*if (Memory::Read_U32(netAdhocDiscoverBufAddr + 0x70) >= 0) {
6097 LinkDiscoverSkip(Memory::Read_U32(netAdhocDiscoverBufAddr + 0x70)); //sceNetAdhocctl_lib_1C679240
6098 Memory::Write_U32(0xffffffff, netAdhocDiscoverBufAddr + 0x70);
6099 }
6100 Memory::Write_U32(0, netAdhocDiscoverBufAddr + 0x80);
6101 Memory::Write_U32(0, netAdhocDiscoverBufAddr + 0xA8);
6102 */
6103 netAdhocDiscoverStatus = NET_ADHOC_DISCOVER_STATUS_NONE;
6104 //if (netAdhocDiscoverParam) netAdhocDiscoverParam->result = NET_ADHOC_DISCOVER_RESULT_NO_PEER_FOUND; // Test: Using result = NET_ADHOC_DISCOVER_RESULT_NO_PEER_FOUND will trigger Legend Of The Dragon to call sceNetAdhocctlGetPeerList after DiscoverTerm
6105 if (Memory::IsValidAddress(netAdhocDiscoverBufAddr)) {
6106 userMemory.Free(netAdhocDiscoverBufAddr);
6107 netAdhocDiscoverBufAddr = 0;
6108 }
6109 netAdhocDiscoverIsStopping = false;
6110 return 0;
6111 }
6112
sceNetAdhocDiscoverGetStatus()6113 int sceNetAdhocDiscoverGetStatus() {
6114 DEBUG_LOG(SCENET, "UNIMPL sceNetAdhocDiscoverGetStatus() at %08x", currentMIPS->pc);
6115 if (sceKernelCheckThreadStack() < 0x00000FF0)
6116 return 0x80410005;
6117 /*
6118 if (Memory::Read_U32(netAdhocDiscoverBufAddr + 0x80) <= 0)
6119 return 0;
6120 if (Memory::Read_U32(netAdhocDiscoverBufAddr + 0x80) <= 0x13)
6121 return 1;
6122 if (Memory::Read_U32(netAdhocDiscoverBufAddr + 0x80) == 0x13)
6123 return 2;
6124 */
6125 return hleLogDebug(SCENET, netAdhocDiscoverStatus); // Returning 2 will trigger Legend Of The Dragon to call sceNetAdhocctlGetPeerList (only happened if it was the first sceNetAdhocDiscoverGetStatus after sceNetAdhocDiscoverInitStart)
6126 }
6127
sceNetAdhocDiscoverRequestSuspend()6128 int sceNetAdhocDiscoverRequestSuspend()
6129 {
6130 ERROR_LOG_REPORT_ONCE(sceNetAdhocDiscoverRequestSuspend, SCENET, "UNIMPL sceNetAdhocDiscoverRequestSuspend() at %08x", currentMIPS->pc);
6131 // FIXME: Not sure what is this syscall used for, may be related to Sleep Mode and can be triggered by using Power/Hold Switch? (based on what's written on Dissidia 012)
6132 if (sceKernelCheckThreadStack() < 0x00000FF0)
6133 return 0x80410005;
6134 /*
6135 if (Memory::Read_U32(netAdhocDiscoverBufAddr + 0xA4) == 0)
6136 return 0x80411303; // Already Suspended?
6137 if (Memory::Read_U32(netAdhocDiscoverBufAddr + 0x80) != 0)
6138 return 0x80411303; // Already Suspended?
6139 int ret = sceNetAdhocctl_lib_1572422C();
6140 if (ret >= 0)
6141 Memory::Write_U32(0, netAdhocDiscoverBufAddr + 0xA4);
6142 return ret;
6143 */
6144 // Since we don't know what this supposed to do, and we currently don't have a working AdhocDiscover yet, may be we should cancel the progress for now?
6145 netAdhocDiscoverIsStopping = true;
6146 return hleLogError(SCENET, 0);
6147 }
6148
sceNetAdhocDiscoverUpdate()6149 int sceNetAdhocDiscoverUpdate() {
6150 DEBUG_LOG(SCENET, "UNIMPL sceNetAdhocDiscoverUpdate() at %08x", currentMIPS->pc);
6151 if (sceKernelCheckThreadStack() < 0x00000FF0)
6152 return 0x80410005;
6153
6154 // TODO: Use switch case for each Step
6155 if (netAdhocDiscoverStatus == NET_ADHOC_DISCOVER_STATUS_IN_PROGRESS) {
6156 //u64 now = CoreTiming::GetGlobalTimeUsScaled();
6157 if (netAdhocDiscoverIsStopping /*|| now >= netAdhocDiscoverStartTime + DISCOVER_DURATION_US*/) {
6158 // Fake a successful completion after some time (or when detecting another player in the same Group?)
6159 netAdhocDiscoverStatus = NET_ADHOC_DISCOVER_STATUS_COMPLETED;
6160 if (netAdhocDiscoverParam)
6161 netAdhocDiscoverParam->result = NET_ADHOC_DISCOVER_RESULT_CANCELED; // netAdhocDiscoverIsStopping ? NET_ADHOC_DISCOVER_RESULT_CANCELED : NET_ADHOC_DISCOVER_RESULT_PEER_FOUND;
6162 }
6163 }
6164 return hleDelayResult(hleLogDebug(SCENET, 0/*netAdhocDiscoverBufAddr*/), "adhoc discover update", 300); // FIXME: Based on JPCSP+prx, it seems to be returning a pointer to the internal buffer/struct (only when status = 1 ?), But when i stepped the code it returns 0 (might be a bug on JPCSP LLE Logging?)
6165 }
6166
6167 const HLEFunction sceNetAdhocDiscover[] = {
6168 {0X941B3877, &WrapI_U<sceNetAdhocDiscoverInitStart>, "sceNetAdhocDiscoverInitStart", 'i', "x" },
6169 {0X52DE1B97, &WrapI_V<sceNetAdhocDiscoverUpdate>, "sceNetAdhocDiscoverUpdate", 'i', "" },
6170 {0X944DDBC6, &WrapI_V<sceNetAdhocDiscoverGetStatus>, "sceNetAdhocDiscoverGetStatus", 'i', "" },
6171 {0XA2246614, &WrapI_V<sceNetAdhocDiscoverTerm>, "sceNetAdhocDiscoverTerm", 'i', "" },
6172 {0XF7D13214, &WrapI_V<sceNetAdhocDiscoverStop>, "sceNetAdhocDiscoverStop", 'i', "" },
6173 {0XA423A21B, &WrapI_V<sceNetAdhocDiscoverRequestSuspend>, "sceNetAdhocDiscoverRequestSuspend", 'i', "" },
6174 };
6175
Register_sceNetAdhoc()6176 void Register_sceNetAdhoc() {
6177 RegisterModule("sceNetAdhoc", ARRAY_SIZE(sceNetAdhoc), sceNetAdhoc);
6178 RegisterModule("sceNetAdhocMatching", ARRAY_SIZE(sceNetAdhocMatching), sceNetAdhocMatching);
6179 RegisterModule("sceNetAdhocDiscover", ARRAY_SIZE(sceNetAdhocDiscover), sceNetAdhocDiscover);
6180 RegisterModule("sceNetAdhocctl", ARRAY_SIZE(sceNetAdhocctl), sceNetAdhocctl);
6181 }
6182
6183 /**
6184 * Broadcast Ping Message to other Matching Users
6185 * @param context Matching Context Pointer
6186 */
broadcastPingMessage(SceNetAdhocMatchingContext * context)6187 void broadcastPingMessage(SceNetAdhocMatchingContext * context)
6188 {
6189 // Ping Opcode
6190 uint8_t ping = PSP_ADHOC_MATCHING_PACKET_PING;
6191
6192 // Send Broadcast
6193 // FIXME: Not sure whether this PING supposed to be sent only to AdhocMatching members or to everyone in Adhocctl Group, since we already pinging the AdhocServer to avoid getting kicked out of Adhocctl Group
6194 peerlock.lock();
6195 auto peer = friends; // Use context->peerlist if only need to send to AdhocMatching members
6196 for (; peer != NULL; peer = peer->next) {
6197 // Skipping soon to be removed peer
6198 if (peer->last_recv == 0)
6199 continue;
6200
6201 u16_le port = context->port;
6202 auto it = (*context->peerPort).find(peer->mac_addr);
6203 if (it != (*context->peerPort).end())
6204 port = it->second;
6205
6206 context->socketlock->lock();
6207 sceNetAdhocPdpSend(context->socket, (const char*)&peer->mac_addr, port, &ping, sizeof(ping), 0, ADHOC_F_NONBLOCK);
6208 context->socketlock->unlock();
6209 }
6210 peerlock.unlock();
6211 }
6212
6213 /**
6214 * Broadcast Hello Message to other Matching Users
6215 * @param context Matching Context Pointer
6216 */
broadcastHelloMessage(SceNetAdhocMatchingContext * context)6217 void broadcastHelloMessage(SceNetAdhocMatchingContext * context)
6218 {
6219 static uint8_t * hello = NULL;
6220 static int32_t len = -5;
6221
6222 // Allocate Hello Message Buffer, reuse when necessary
6223 if ((int32_t)context->hellolen > len) {
6224 uint8_t* tmp = (uint8_t *)realloc(hello, 5LL + context->hellolen);
6225 if (tmp != NULL) {
6226 hello = tmp;
6227 len = context->hellolen;
6228 }
6229 }
6230
6231 // Allocated Hello Message Buffer
6232 if (hello != NULL)
6233 {
6234 // Hello Opcode
6235 hello[0] = PSP_ADHOC_MATCHING_PACKET_HELLO;
6236
6237 // Hello Data Length (have to memcpy this to avoid cpu alignment crash)
6238 memcpy(hello + 1, &context->hellolen, sizeof(context->hellolen));
6239
6240 // FIXME: When using JPCSP + prx files the data being sent have a header of 12 bytes instead of 5 bytes:
6241 // [01(always 1? size of the next data? or combined with next byte as U16_BE opcode?) 01(matching opcode, or combined with previous byte as U16_BE opcode?) 01 E0(size of next data + hello data in big-endian/U16_BE) 00 0F 42 40(U32_BE? time?) 00 0F 42 40(U32_BE? time?)],
6242 // followed by hello data (0x1D8 bytes of opt data, based on Ys vs. Sora no Kiseki), and followed by 16 bytes of (optional?) footer [01 00 00 .. 00 00](footer doesn't exist if the size after opcode is 00 00)
6243
6244 // Copy Hello Data
6245 if (context->hellolen > 0) memcpy(hello + 5, context->hello, context->hellolen);
6246
6247 std::string hellohex;
6248 DataToHexString(10, 0, context->hello, context->hellolen, &hellohex);
6249 DEBUG_LOG(SCENET, "HELLO Dump (%d bytes):\n%s", context->hellolen, hellohex.c_str());
6250
6251 // Send Broadcast, so everyone know we have a room here
6252 peerlock.lock();
6253 SceNetAdhocctlPeerInfo* peer = friends;
6254 for (; peer != NULL; peer = peer->next) {
6255 // Skipping soon to be removed peer
6256 if (peer->last_recv == 0)
6257 continue;
6258
6259 u16_le port = context->port;
6260 auto it = (*context->peerPort).find(peer->mac_addr);
6261 if (it != (*context->peerPort).end())
6262 port = it->second;
6263
6264 context->socketlock->lock();
6265 sceNetAdhocPdpSend(context->socket, (const char*)&peer->mac_addr, port, hello, 5 + context->hellolen, 0, ADHOC_F_NONBLOCK);
6266 context->socketlock->unlock();
6267 }
6268 peerlock.unlock();
6269
6270 // Free Memory, not needed since it may be reused again later
6271 //free(hello);
6272 }
6273 }
6274
6275 /**
6276 * Send Accept Packet to Player
6277 * @param context Matching Context Pointer
6278 * @param mac Target Player MAC
6279 * @param optlen Optional Data Length
6280 * @param opt Optional Data
6281 */
sendAcceptPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * mac,int optlen,void * opt)6282 void sendAcceptPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac, int optlen, void * opt)
6283 {
6284 // Lock the peer
6285 std::lock_guard<std::recursive_mutex> peer_guard(peerlock);
6286
6287 // Find Peer
6288 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, mac);
6289
6290 // Found Peer
6291 if (peer != NULL && (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD || peer->state == PSP_ADHOC_MATCHING_PEER_P2P))
6292 {
6293 // Required Sibling Buffer
6294 uint32_t siblingbuflen = 0;
6295
6296 // Parent Mode
6297 if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT) siblingbuflen = sizeof(SceNetEtherAddr) * (countConnectedPeers(context) - 2);
6298
6299 // Sibling Count
6300 int siblingcount = siblingbuflen / sizeof(SceNetEtherAddr);
6301
6302 // Allocate Accept Message Buffer
6303 uint8_t * accept = (uint8_t *)malloc(9LL + optlen + siblingbuflen);
6304
6305 // Allocated Accept Message Buffer
6306 if (accept != NULL)
6307 {
6308 // Accept Opcode
6309 accept[0] = PSP_ADHOC_MATCHING_PACKET_ACCEPT;
6310
6311 // Optional Data Length
6312 memcpy(accept + 1, &optlen, sizeof(optlen));
6313
6314 // Sibling Count
6315 memcpy(accept + 5, &siblingcount, sizeof(siblingcount));
6316
6317 // Copy Optional Data
6318 if (optlen > 0) memcpy(accept + 9, opt, optlen);
6319
6320 // Parent Mode Extra Data required
6321 if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && siblingcount > 0)
6322 {
6323 // Create MAC Array Pointer
6324 uint8_t * siblingmacs = (uint8_t *)(accept + 9 + optlen);
6325
6326 // MAC Writing Pointer
6327 int i = 0;
6328
6329 // Iterate Peer List
6330 SceNetAdhocMatchingMemberInternal * item = context->peerlist;
6331 for (; item != NULL; item = item->next)
6332 {
6333 // Ignore Target
6334 if (item == peer) continue;
6335
6336 // Copy Child MAC
6337 if (item->state == PSP_ADHOC_MATCHING_PEER_CHILD)
6338 {
6339 // Clone MAC the stupid memcpy way to shut up PSP CPU
6340 memcpy(siblingmacs + sizeof(SceNetEtherAddr) * i++, &item->mac, sizeof(SceNetEtherAddr));
6341 }
6342 }
6343 }
6344
6345 // Send Data
6346 context->socketlock->lock();
6347 sceNetAdhocPdpSend(context->socket, (const char*)mac, (*context->peerPort)[*mac], accept, 9 + optlen + siblingbuflen, 0, ADHOC_F_NONBLOCK);
6348 context->socketlock->unlock();
6349
6350 // Free Memory
6351 free(accept);
6352
6353 // Spawn Local Established Event
6354 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_ESTABLISHED, mac, 0, NULL);
6355 }
6356 }
6357 }
6358
6359 /**
6360 * Send Join Packet to Player
6361 * @param context Matching Context Pointer
6362 * @param mac Target Player MAC
6363 * @param optlen Optional Data Length
6364 * @param opt Optional Data
6365 */
sendJoinPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * mac,int optlen,void * opt)6366 void sendJoinPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac, int optlen, void * opt)
6367 {
6368 // Lock the peer
6369 std::lock_guard<std::recursive_mutex> peer_guard(peerlock);
6370
6371 // Find Peer
6372 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, mac);
6373
6374 // Valid Peer
6375 if (peer != NULL && peer->state == PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST)
6376 {
6377 // Allocate Join Message Buffer
6378 uint8_t * join = (uint8_t *)malloc(5LL + optlen);
6379
6380 // Allocated Join Message Buffer
6381 if (join != NULL)
6382 {
6383 // Join Opcode
6384 join[0] = PSP_ADHOC_MATCHING_PACKET_JOIN;
6385
6386 // Optional Data Length
6387 memcpy(join + 1, &optlen, sizeof(optlen));
6388
6389 // Copy Optional Data
6390 if (optlen > 0) memcpy(join + 5, opt, optlen);
6391
6392 // Send Data
6393 context->socketlock->lock();
6394 sceNetAdhocPdpSend(context->socket, (const char*)mac, (*context->peerPort)[*mac], join, 5 + optlen, 0, ADHOC_F_NONBLOCK);
6395 context->socketlock->unlock();
6396
6397 // Free Memory
6398 free(join);
6399 }
6400 }
6401 }
6402
6403 /**
6404 * Send Cancel Packet to Player
6405 * @param context Matching Context Pointer
6406 * @param mac Target Player MAC
6407 * @param optlen Optional Data Length
6408 * @param opt Optional Data
6409 */
sendCancelPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * mac,int optlen,void * opt)6410 void sendCancelPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac, int optlen, void * opt)
6411 {
6412 // Lock the peer
6413 std::lock_guard<std::recursive_mutex> peer_guard(peerlock);
6414
6415 // Allocate Cancel Message Buffer
6416 uint8_t * cancel = (uint8_t *)malloc(5LL + optlen);
6417
6418 // Allocated Cancel Message Buffer
6419 if (cancel != NULL)
6420 {
6421 // Cancel Opcode
6422 cancel[0] = PSP_ADHOC_MATCHING_PACKET_CANCEL;
6423
6424 // Optional Data Length
6425 memcpy(cancel + 1, &optlen, sizeof(optlen));
6426
6427 // Copy Optional Data
6428 if (optlen > 0) memcpy(cancel + 5, opt, optlen);
6429
6430 // Send Data
6431 context->socketlock->lock();
6432 sceNetAdhocPdpSend(context->socket, (const char*)mac, (*context->peerPort)[*mac], cancel, 5 + optlen, 0, ADHOC_F_NONBLOCK);
6433 context->socketlock->unlock();
6434
6435 // Free Memory
6436 free(cancel);
6437 }
6438
6439 // Find Peer
6440 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, mac);
6441
6442 // Found Peer
6443 if (peer != NULL)
6444 {
6445 // Child Mode Fallback - Delete All
6446 if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD)
6447 {
6448 // Delete Peer List
6449 clearPeerList(context);
6450 }
6451
6452 // Delete Peer
6453 else deletePeer(context, peer);
6454 }
6455 }
6456
6457 /**
6458 * Send Bulk Data Packet to Player
6459 * @param context Matching Context Pointer
6460 * @param mac Target Player MAC
6461 * @param datalen Data Length
6462 * @param data Data
6463 */
sendBulkDataPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * mac,int datalen,void * data)6464 void sendBulkDataPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac, int datalen, void * data)
6465 {
6466 // Lock the peer
6467 std::lock_guard<std::recursive_mutex> peer_guard(peerlock);
6468
6469 // Find Peer
6470 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, mac);
6471
6472 // Valid Peer (rest is already checked in send.c)
6473 if (peer != NULL)
6474 {
6475 // Don't send if it's aborted
6476 //if (peer->sending == 0) return;
6477
6478 // Allocate Send Message Buffer
6479 uint8_t * send = (uint8_t *)malloc(5LL + datalen);
6480
6481 // Allocated Send Message Buffer
6482 if (send != NULL)
6483 {
6484 // Send Opcode
6485 send[0] = PSP_ADHOC_MATCHING_PACKET_BULK;
6486
6487 // Data Length
6488 memcpy(send + 1, &datalen, sizeof(datalen));
6489
6490 // Copy Data
6491 memcpy(send + 5, data, datalen);
6492
6493 // Send Data
6494 context->socketlock->lock();
6495 sceNetAdhocPdpSend(context->socket, (const char*)mac, (*context->peerPort)[*mac], send, 5 + datalen, 0, ADHOC_F_NONBLOCK);
6496 context->socketlock->unlock();
6497
6498 // Free Memory
6499 free(send);
6500
6501 // Remove Busy Bit from Peer
6502 peer->sending = 0;
6503
6504 // Spawn Data Event
6505 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_DATA_ACK, mac, 0, NULL);
6506 }
6507 }
6508 }
6509
6510 /**
6511 * Tell Established Peers of new Child
6512 * @param context Matching Context Pointer
6513 * @param mac New Child's MAC
6514 */
sendBirthPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * mac)6515 void sendBirthPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac)
6516 {
6517 // Lock the peer
6518 std::lock_guard<std::recursive_mutex> peer_guard(peerlock);
6519
6520 // Find Newborn Child
6521 SceNetAdhocMatchingMemberInternal * newborn = findPeer(context, mac);
6522
6523 // Found Newborn Child
6524 if (newborn != NULL)
6525 {
6526 // Packet Buffer
6527 uint8_t packet[7];
6528
6529 // Set Opcode
6530 packet[0] = PSP_ADHOC_MATCHING_PACKET_BIRTH;
6531
6532 // Set Newborn MAC
6533 memcpy(packet + 1, mac, sizeof(SceNetEtherAddr));
6534
6535 // Iterate Peers
6536 SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
6537 for (; peer != NULL; peer = peer->next)
6538 {
6539 // Skip Newborn Child
6540 if (peer == newborn) continue;
6541
6542 // Send only to children
6543 if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD)
6544 {
6545 // Send Packet
6546 context->socketlock->lock();
6547 int sent = sceNetAdhocPdpSend(context->socket, (const char*)&peer->mac, (*context->peerPort)[peer->mac], packet, sizeof(packet), 0, ADHOC_F_NONBLOCK);
6548 context->socketlock->unlock();
6549
6550 // Log Send Success
6551 if (sent >= 0)
6552 INFO_LOG(SCENET, "InputLoop: Sending BIRTH [%s] to %s", mac2str(mac).c_str(), mac2str(&peer->mac).c_str());
6553 else
6554 WARN_LOG(SCENET, "InputLoop: Failed to Send BIRTH [%s] to %s", mac2str(mac).c_str(), mac2str(&peer->mac).c_str());
6555 }
6556 }
6557 }
6558 }
6559
6560 /**
6561 * Tell Established Peers of abandoned Child
6562 * @param context Matching Context Pointer
6563 * @param mac Dead Child's MAC
6564 */
sendDeathPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * mac)6565 void sendDeathPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac)
6566 {
6567 // Lock the peer
6568 std::lock_guard<std::recursive_mutex> peer_guard(peerlock);
6569
6570 // Find abandoned Child
6571 SceNetAdhocMatchingMemberInternal * deadkid = findPeer(context, mac);
6572
6573 // Found abandoned Child
6574 if (deadkid != NULL)
6575 {
6576 // Packet Buffer
6577 uint8_t packet[7];
6578
6579 // Set abandoned Child MAC
6580 memcpy(packet + 1, mac, sizeof(SceNetEtherAddr));
6581
6582 // Iterate Peers
6583 SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
6584 for (; peer != NULL; peer = peer->next)
6585 {
6586 // Skip dead Child? Or May be we should also tells the disconnected Child, that they have been disconnected from the Host (in the case they were disconnected because they went to PPSSPP settings for too long)
6587 if (peer == deadkid) {
6588 // Set Opcode
6589 packet[0] = PSP_ADHOC_MATCHING_PACKET_BYE;
6590
6591 // Send Bye Packet
6592 context->socketlock->lock();
6593 sceNetAdhocPdpSend(context->socket, (const char*)&peer->mac, (*context->peerPort)[peer->mac], packet, sizeof(packet[0]), 0, ADHOC_F_NONBLOCK);
6594 context->socketlock->unlock();
6595 }
6596 else
6597 // Send to other children
6598 if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD)
6599 {
6600 // Set Opcode
6601 packet[0] = PSP_ADHOC_MATCHING_PACKET_DEATH;
6602
6603 // Send Death Packet
6604 context->socketlock->lock();
6605 sceNetAdhocPdpSend(context->socket, (const char*)&peer->mac, (*context->peerPort)[peer->mac], packet, sizeof(packet), 0, ADHOC_F_NONBLOCK);
6606 context->socketlock->unlock();
6607 }
6608 }
6609
6610 // Delete Peer
6611 deletePeer(context, deadkid);
6612 }
6613 }
6614
6615 /**
6616 * Tell Established Peers that we're shutting the Networking Layer down
6617 * @param context Matching Context Pointer
6618 */
sendByePacket(SceNetAdhocMatchingContext * context)6619 void sendByePacket(SceNetAdhocMatchingContext * context)
6620 {
6621 // Lock the peer
6622 std::lock_guard<std::recursive_mutex> peer_guard(peerlock);
6623
6624 // Iterate Peers
6625 SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
6626 for (; peer != NULL; peer = peer->next)
6627 {
6628 // Peer of Interest
6629 if (peer->state == PSP_ADHOC_MATCHING_PEER_PARENT || peer->state == PSP_ADHOC_MATCHING_PEER_CHILD || peer->state == PSP_ADHOC_MATCHING_PEER_P2P)
6630 {
6631 // Bye Opcode
6632 uint8_t opcode = PSP_ADHOC_MATCHING_PACKET_BYE;
6633
6634 // Send Bye Packet
6635 context->socketlock->lock();
6636 sceNetAdhocPdpSend(context->socket, (const char*)&peer->mac, (*context->peerPort)[peer->mac], &opcode, sizeof(opcode), 0, ADHOC_F_NONBLOCK);
6637 context->socketlock->unlock();
6638 }
6639 }
6640 }
6641
6642 /**
6643 * Handle Ping Packet
6644 * @param context Matching Context Pointer
6645 * @param sendermac Packet Sender MAC
6646 */
actOnPingPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * sendermac)6647 void actOnPingPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac)
6648 {
6649 // Find Peer
6650 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
6651
6652 // Found Peer
6653 if (peer != NULL)
6654 {
6655 // Update Receive Timer
6656 peer->lastping = CoreTiming::GetGlobalTimeUsScaled(); //time_now_d()*1000000.0;
6657 }
6658 }
6659
6660 /**
6661 * Handle Hello Packet
6662 * @param context Matching Context Pointer
6663 * @param sendermac Packet Sender MAC
6664 * @param length Packet Length
6665 */
actOnHelloPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * sendermac,int32_t length)6666 void actOnHelloPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, int32_t length)
6667 {
6668 // Interested in Hello Data
6669 if ((context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && findParent(context) == NULL) || (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && findP2P(context) == NULL))
6670 {
6671 // Complete Packet Header available
6672 if (length >= 5)
6673 {
6674 // Extract Optional Data Length
6675 int optlen = 0; memcpy(&optlen, context->rxbuf + 1, sizeof(optlen));
6676
6677 // Complete Valid Packet available
6678 if (optlen >= 0 && length >= (5 + optlen))
6679 {
6680 // Set Default Null Data
6681 void * opt = NULL;
6682
6683 // Extract Optional Data Pointer
6684 if (optlen > 0) opt = context->rxbuf + 5;
6685
6686 // Find Peer
6687 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
6688
6689 // Peer not found
6690 if (peer == NULL)
6691 {
6692 // Allocate Memory
6693 peer = (SceNetAdhocMatchingMemberInternal *)malloc(sizeof(SceNetAdhocMatchingMemberInternal));
6694
6695 // Allocated Memory
6696 if (peer != NULL)
6697 {
6698 // Clear Memory
6699 memset(peer, 0, sizeof(SceNetAdhocMatchingMemberInternal));
6700
6701 // Copy Sender MAC
6702 peer->mac = *sendermac;
6703
6704 // Set Peer State
6705 peer->state = PSP_ADHOC_MATCHING_PEER_OFFER;
6706
6707 // Initialize Ping Timer
6708 peer->lastping = CoreTiming::GetGlobalTimeUsScaled(); //time_now_d()*1000000.0;
6709
6710 peerlock.lock();
6711 // Link Peer into List
6712 peer->next = context->peerlist;
6713 context->peerlist = peer;
6714 peerlock.unlock();
6715 }
6716 }
6717
6718 // Peer available now
6719 if (peer != NULL && peer->state != PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST && peer->state != PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST)
6720 {
6721 std::string hellohex;
6722 DataToHexString(10, 0, (u8*)opt, optlen, &hellohex);
6723 DEBUG_LOG(SCENET, "HELLO Dump (%d bytes):\n%s", optlen, hellohex.c_str());
6724
6725 // Spawn Hello Event. FIXME: HELLO event should not be triggered in the middle of joining? This will cause Bleach 7 to Cancel the join request
6726 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_HELLO, sendermac, optlen, opt);
6727 }
6728 }
6729 }
6730 }
6731 }
6732
6733 /**
6734 * Handle Join Packet
6735 * @param context Matching Context Pointer
6736 * @param sendermac Packet Sender MAC
6737 * @param length Packet Length
6738 */
actOnJoinPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * sendermac,int32_t length)6739 void actOnJoinPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, int32_t length)
6740 {
6741 // Not a child mode context
6742 if (context->mode != PSP_ADHOC_MATCHING_MODE_CHILD)
6743 {
6744 // We still got a unoccupied slot in our room (Parent / P2P)
6745 if ((context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && countChildren(context) < (context->maxpeers - 1)) || (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && findP2P(context) == NULL))
6746 {
6747 // Complete Packet Header available
6748 if (length >= 5)
6749 {
6750 // Extract Optional Data Length
6751 int optlen = 0; memcpy(&optlen, context->rxbuf + 1, sizeof(optlen));
6752
6753 // Complete Valid Packet available
6754 if (optlen >= 0 && length >= (5 + optlen))
6755 {
6756 // Set Default Null Data
6757 void * opt = NULL;
6758
6759 // Extract Optional Data Pointer
6760 if (optlen > 0) opt = context->rxbuf + 5;
6761
6762 // Find Peer
6763 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
6764
6765 // If we got the peer in the table already and are a parent, there is nothing left to be done.
6766 // This is because the only way a parent can know of a child is via a join request...
6767 // If we thus know of a possible child, then we already had a previous join request thus no need for double tapping.
6768 if (peer != NULL && peer->lastping != 0 && context->mode == PSP_ADHOC_MATCHING_MODE_PARENT) {
6769 WARN_LOG(SCENET, "Join Event(2) Ignored");
6770 return;
6771 }
6772
6773 // New Peer
6774 if (peer == NULL)
6775 {
6776 // Allocate Memory
6777 peer = (SceNetAdhocMatchingMemberInternal *)malloc(sizeof(SceNetAdhocMatchingMemberInternal));
6778
6779 // Allocated Memory
6780 if (peer != NULL)
6781 {
6782 // Clear Memory
6783 memset(peer, 0, sizeof(SceNetAdhocMatchingMemberInternal));
6784
6785 // Copy Sender MAC
6786 peer->mac = *sendermac;
6787
6788 // Set Peer State
6789 peer->state = PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST;
6790
6791 // Initialize Ping Timer
6792 peer->lastping = CoreTiming::GetGlobalTimeUsScaled(); //time_now_d()*1000000.0;
6793
6794 peerlock.lock();
6795 // Link Peer into List
6796 peer->next = context->peerlist;
6797 context->peerlist = peer;
6798 peerlock.unlock();
6799
6800 // Spawn Request Event
6801 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_REQUEST, sendermac, optlen, opt);
6802
6803 // Return Success
6804 return;
6805 }
6806 }
6807
6808 // Existing Peer (this case is only reachable for P2P mode)
6809 else
6810 {
6811 // Set Peer State
6812 peer->state = PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST;
6813
6814 // Initialize Ping Timer
6815 peer->lastping = CoreTiming::GetGlobalTimeUsScaled();
6816
6817 // Spawn Request Event
6818 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_REQUEST, sendermac, optlen, opt);
6819
6820 // Return Success
6821 return;
6822 }
6823 }
6824 }
6825 }
6826 WARN_LOG(SCENET, "Join Event(2) Rejected");
6827 // Auto-Reject Player
6828 sendCancelPacket(context, sendermac, 0, NULL);
6829 }
6830 }
6831
6832 /**
6833 * Handle Accept Packet
6834 * @param context Matching Context Pointer
6835 * @param sendermac Packet Sender MAC
6836 * @param length Packet Length
6837 */
actOnAcceptPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * sendermac,uint32_t length)6838 void actOnAcceptPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, uint32_t length)
6839 {
6840 // Not a parent context
6841 if (context->mode != PSP_ADHOC_MATCHING_MODE_PARENT)
6842 {
6843 // Don't have a master yet
6844 if ((context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && findParent(context) == NULL) || (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && findP2P(context) == NULL))
6845 {
6846 // Complete Packet Header available
6847 if (length >= 9)
6848 {
6849 // Extract Optional Data Length
6850 int optlen = 0; memcpy(&optlen, context->rxbuf + 1, sizeof(optlen));
6851
6852 // Extract Sibling Count
6853 int siblingcount = 0; memcpy(&siblingcount, context->rxbuf + 5, sizeof(siblingcount));
6854
6855 // Complete Valid Packet available
6856 if (optlen >= 0 && length >= (9LL + optlen + static_cast<long long>(sizeof(SceNetEtherAddr)) * siblingcount))
6857 {
6858 // Set Default Null Data
6859 void * opt = NULL;
6860
6861 // Extract Optional Data Pointer
6862 if (optlen > 0) opt = context->rxbuf + 9;
6863
6864 // Sibling MAC Array Null Data
6865 SceNetEtherAddr * siblings = NULL;
6866
6867 // Extract Optional Sibling MAC Array
6868 if (siblingcount > 0) siblings = (SceNetEtherAddr *)(context->rxbuf + 9 + optlen);
6869
6870 // Find Outgoing Request
6871 SceNetAdhocMatchingMemberInternal * request = findOutgoingRequest(context);
6872
6873 // We are waiting for a answer to our request...
6874 if (request != NULL)
6875 {
6876 // Find Peer
6877 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
6878
6879 // It's the answer we wanted!
6880 if (request == peer)
6881 {
6882 // Change Peer State
6883 peer->state = (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD) ? (PSP_ADHOC_MATCHING_PEER_PARENT) : (PSP_ADHOC_MATCHING_PEER_P2P);
6884
6885 // Remove Unneeded Peer Information
6886 postAcceptCleanPeerList(context);
6887
6888 // Add Sibling Peers
6889 if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD) {
6890 // Add existing siblings
6891 postAcceptAddSiblings(context, siblingcount, siblings);
6892
6893 // Add Self Peer to the following position (using peer->state = 0 to identify as Self)
6894 addMember(context, &context->mac);
6895 }
6896
6897 // IMPORTANT! The Event Order here is ok!
6898 // Internally the Event Stack appends to the front, so the order will be switched around.
6899
6900 // Spawn Established Event
6901 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_ESTABLISHED, sendermac, 0, NULL);
6902
6903 // Spawn Accept Event
6904 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_ACCEPT, sendermac, optlen, opt);
6905 }
6906 }
6907 }
6908 }
6909 }
6910 }
6911 }
6912
6913 /**
6914 * Handle Cancel Packet
6915 * @param context Matching Context Pointer
6916 * @param sendermac Packet Sender MAC
6917 * @param length Packet Length
6918 */
actOnCancelPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * sendermac,int32_t length)6919 void actOnCancelPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, int32_t length)
6920 {
6921 // Find Peer
6922 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
6923
6924 // Interest Condition fulfilled
6925 if (peer != NULL)
6926 {
6927 // Complete Packet Header available
6928 if (length >= 5)
6929 {
6930 // Extract Optional Data Length
6931 int optlen = 0; memcpy(&optlen, context->rxbuf + 1, sizeof(optlen));
6932
6933 // Complete Valid Packet available
6934 if (optlen >= 0 && length >= (5 + optlen))
6935 {
6936 // Set Default Null Data
6937 void * opt = NULL;
6938
6939 // Extract Optional Data Pointer
6940 if (optlen > 0) opt = context->rxbuf + 5;
6941
6942 // Get Outgoing Join Request
6943 SceNetAdhocMatchingMemberInternal* request = findOutgoingRequest(context);
6944
6945 // Child Mode
6946 if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD)
6947 {
6948 // Get Parent
6949 SceNetAdhocMatchingMemberInternal* parent = findParent(context);
6950
6951 // Join Request denied
6952 if (request == peer)
6953 {
6954 // Spawn Deny Event
6955 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_DENY, sendermac, optlen, opt);
6956
6957 // Delete Peer from List
6958 deletePeer(context, peer);
6959 }
6960
6961 // Kicked from Room
6962 else if (parent == peer)
6963 {
6964 // Iterate Peers
6965 SceNetAdhocMatchingMemberInternal * item = context->peerlist;
6966 for (; item != NULL; item = item->next)
6967 {
6968 // Established Peer
6969 if (item->state == PSP_ADHOC_MATCHING_PEER_CHILD || item->state == PSP_ADHOC_MATCHING_PEER_PARENT)
6970 {
6971 // Spawn Leave / Kick Event
6972 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_LEAVE, &item->mac, optlen, opt);
6973 }
6974 }
6975
6976 // Delete Peer from List
6977 clearPeerList(context);
6978 }
6979 }
6980
6981 // Parent Mode
6982 else if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT)
6983 {
6984 // Cancel Join Request
6985 if (peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST)
6986 {
6987 // Spawn Request Cancel Event
6988 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_CANCEL, sendermac, optlen, opt);
6989
6990 // Delete Peer from List
6991 deletePeer(context, peer);
6992 }
6993
6994 // Leave Room
6995 else if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD)
6996 {
6997 // Spawn Leave / Kick Event
6998 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_LEAVE, sendermac, optlen, opt);
6999
7000 // Delete Peer from List
7001 deletePeer(context, peer);
7002 }
7003 }
7004
7005 // P2P Mode
7006 else
7007 {
7008 // Get P2P Partner
7009 SceNetAdhocMatchingMemberInternal* p2p = findP2P(context);
7010
7011 // Join Request denied
7012 if (request == peer)
7013 {
7014 // Spawn Deny Event
7015 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_DENY, sendermac, optlen, opt);
7016
7017 // Delete Peer from List
7018 deletePeer(context, peer);
7019 }
7020
7021 // Kicked from Room
7022 else if (p2p == peer)
7023 {
7024 // Spawn Leave / Kick Event
7025 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_LEAVE, sendermac, optlen, opt);
7026
7027 // Delete Peer from List
7028 deletePeer(context, peer);
7029 }
7030
7031 // Cancel Join Request
7032 else if (peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST)
7033 {
7034 // Spawn Request Cancel Event
7035 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_CANCEL, sendermac, optlen, opt);
7036
7037 // Delete Peer from List
7038 deletePeer(context, peer);
7039 }
7040 }
7041 }
7042 }
7043 }
7044 }
7045
7046 /**
7047 * Handle Bulk Data Packet
7048 * @param context Matching Context Pointer
7049 * @param sendermac Packet Sender MAC
7050 * @param length Packet Length
7051 */
actOnBulkDataPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * sendermac,int32_t length)7052 void actOnBulkDataPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, int32_t length)
7053 {
7054 // Find Peer
7055 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
7056
7057 // Established Peer
7058 if (peer != NULL && (
7059 (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) ||
7060 (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD || peer->state == PSP_ADHOC_MATCHING_PEER_PARENT)) ||
7061 (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && peer->state == PSP_ADHOC_MATCHING_PEER_P2P)))
7062 {
7063 // Complete Packet Header available
7064 if (length > 5)
7065 {
7066 // Extract Data Length
7067 int datalen = 0; memcpy(&datalen, context->rxbuf + 1, sizeof(datalen));
7068
7069 // Complete Valid Packet available
7070 if (datalen > 0 && length >= (5 + datalen))
7071 {
7072 // Extract Data
7073 void * data = context->rxbuf + 5;
7074
7075 // Spawn Data Event
7076 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_DATA, sendermac, datalen, data);
7077 }
7078 }
7079 }
7080 }
7081
7082 /**
7083 * Handle Birth Packet
7084 * @param context Matching Context Pointer
7085 * @param sendermac Packet Sender MAC
7086 * @param length Packet Length
7087 */
actOnBirthPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * sendermac,uint32_t length)7088 void actOnBirthPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, uint32_t length)
7089 {
7090 // Find Peer
7091 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
7092
7093 // Valid Circumstances
7094 if (peer != NULL && context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && peer == findParent(context))
7095 {
7096 // Complete Packet available
7097 if (length >= (1 + sizeof(SceNetEtherAddr)))
7098 {
7099 // Extract Child MAC
7100 SceNetEtherAddr mac;
7101 memcpy(&mac, context->rxbuf + 1, sizeof(SceNetEtherAddr));
7102
7103 // Allocate Memory
7104 SceNetAdhocMatchingMemberInternal * sibling = (SceNetAdhocMatchingMemberInternal *)malloc(sizeof(SceNetAdhocMatchingMemberInternal));
7105
7106 // Allocated Memory
7107 if (sibling != NULL)
7108 {
7109 // Clear Memory
7110 memset(sibling, 0, sizeof(SceNetAdhocMatchingMemberInternal));
7111
7112 // Save MAC Address
7113 sibling->mac = mac;
7114
7115 // Set Peer State
7116 sibling->state = PSP_ADHOC_MATCHING_PEER_CHILD;
7117
7118 // Initialize Ping Timer
7119 sibling->lastping = CoreTiming::GetGlobalTimeUsScaled(); //time_now_d()*1000000.0;
7120
7121 peerlock.lock();
7122
7123 // Link Peer
7124 sibling->next = context->peerlist;
7125 context->peerlist = sibling;
7126
7127 peerlock.unlock();
7128
7129 // Spawn Established Event. FIXME: ESTABLISHED event should only be triggered for Parent/P2P peer?
7130 //spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_ESTABLISHED, &sibling->mac, 0, NULL);
7131 }
7132 }
7133 }
7134 }
7135
7136 /**
7137 * Handle Death Packet
7138 * @param context Matching Context Pointer
7139 * @param sendermac Packet Sender MAC
7140 * @param length Packet Length
7141 */
actOnDeathPacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * sendermac,uint32_t length)7142 void actOnDeathPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, uint32_t length)
7143 {
7144 // Find Peer
7145 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
7146
7147 // Valid Circumstances
7148 if (peer != NULL && context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && peer == findParent(context))
7149 {
7150 // Complete Packet available
7151 if (length >= (1 + sizeof(SceNetEtherAddr)))
7152 {
7153 // Extract Child MAC
7154 SceNetEtherAddr mac;
7155 memcpy(&mac, context->rxbuf + 1, sizeof(SceNetEtherAddr));
7156
7157 // Find Peer
7158 SceNetAdhocMatchingMemberInternal * deadkid = findPeer(context, &mac);
7159
7160 // Valid Sibling
7161 if (deadkid->state == PSP_ADHOC_MATCHING_PEER_CHILD)
7162 {
7163 // Spawn Leave Event
7164 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_LEAVE, &mac, 0, NULL);
7165
7166 // Delete Peer
7167 deletePeer(context, deadkid);
7168 }
7169 }
7170 }
7171 }
7172
7173 /**
7174 * Handle Bye Packet
7175 * @param context Matching Context Pointer
7176 * @param sendermac Packet Sender MAC
7177 */
actOnByePacket(SceNetAdhocMatchingContext * context,SceNetEtherAddr * sendermac)7178 void actOnByePacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac)
7179 {
7180 // Find Peer
7181 SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
7182
7183 // We know this guy
7184 if (peer != NULL)
7185 {
7186 // P2P or Child Bye
7187 if ((context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) ||
7188 (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) ||
7189 (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && peer->state == PSP_ADHOC_MATCHING_PEER_P2P))
7190 {
7191 if (context->mode != PSP_ADHOC_MATCHING_MODE_CHILD) {
7192 // Spawn Leave / Kick Event. FIXME: DISCONNECT event should only be triggered on Parent/P2P mode and for Parent/P2P peer?
7193 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_BYE, sendermac, 0, NULL);
7194 }
7195
7196 // Delete Peer
7197 deletePeer(context, peer);
7198 // Instead of removing peer immediately, We should give a little time before removing the peer and let it timed out? just in case the game is in the middle of communicating with the peer on another thread so it won't recognize it as Unknown peer
7199 //peer->lastping = CoreTiming::GetGlobalTimeUsScaled();
7200 }
7201
7202 // Parent Bye
7203 else if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && peer->state == PSP_ADHOC_MATCHING_PEER_PARENT)
7204 {
7205 // Spawn Leave / Kick Event. FIXME: DISCONNECT event should only be triggered on Parent/P2P mode and for Parent/P2P peer?
7206 spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_BYE, sendermac, 0, NULL);
7207
7208 // Delete Peer from List
7209 clearPeerList(context);
7210 }
7211 }
7212 }
7213
7214
7215 /**
7216 * Matching Event Dispatcher Thread
7217 * @param args sizeof(SceNetAdhocMatchingContext *)
7218 * @param argp SceNetAdhocMatchingContext *
7219 * @return Exit Point is never reached...
7220 */
matchingEventThread(int matchingId)7221 int matchingEventThread(int matchingId)
7222 {
7223 SetCurrentThreadName("MatchingEvent");
7224 // Multithreading Lock
7225 peerlock.lock();
7226 // Cast Context
7227 SceNetAdhocMatchingContext * context = findMatchingContext(matchingId);
7228 // Multithreading Unlock
7229 peerlock.unlock();
7230
7231 // Log Startup
7232 INFO_LOG(SCENET, "EventLoop: Begin of EventLoop[%i] Thread", matchingId);
7233
7234 // Run while needed...
7235 if (context != NULL) {
7236 u32 bufLen = context->rxbuflen; //0;
7237 u32 bufAddr = 0; //= userMemory.Alloc(bufLen); //context->rxbuf;
7238 u32_le * args = context->handlerArgs; //MatchingArgs
7239
7240 while (contexts != NULL && context->eventRunning)
7241 {
7242 // Multithreading Lock
7243 peerlock.lock();
7244 // Cast Context
7245 context = findMatchingContext(matchingId);
7246 // Multithreading Unlock
7247 peerlock.unlock();
7248
7249 // Messages on Stack ready for processing
7250 if (context != NULL && context->event_stack != NULL)
7251 {
7252 // Claim Stack
7253 context->eventlock->lock();
7254
7255 // Iterate Message List
7256 ThreadMessage * msg = context->event_stack;
7257 if (msg != NULL)
7258 {
7259 // Default Optional Data
7260 void* opt = NULL;
7261
7262 // Grab Optional Data
7263 if (msg->optlen > 0) opt = ((u8*)msg) + sizeof(ThreadMessage); //&msg[1]
7264
7265 // Log Matching Events
7266 INFO_LOG(SCENET, "EventLoop[%d]: Matching Event [%d=%s][%s] OptSize=%d", matchingId, msg->opcode, getMatchingEventStr(msg->opcode), mac2str(&msg->mac).c_str(), msg->optlen);
7267
7268 // Unlock to prevent race-condition with other threads due to recursive lock
7269 //context->eventlock->unlock();
7270 // Call Event Handler
7271 //context->handler(context->id, msg->opcode, &msg->mac, msg->optlen, opt);
7272 // Notify Event Handlers
7273 notifyMatchingHandler(context, msg, opt, bufAddr, bufLen, args); // If we're using shared Buffer & Args for All Events We should wait for the Mipscall to be fully executed before processing the next event. GTA VCS need this delay/sleep.
7274
7275 // Give some time before executing the next mipscall to prevent event ACCEPT(6)->ESTABLISH(7) getting reversed After Action ESTABLISH(7)->ACCEPT(6)
7276 // Must Not be delayed too long to prevent desync/disconnect. Not longer than the delays on callback's HLE?
7277 //sleep_ms(10); //sceKernelDelayThread(10000);
7278
7279 // Lock again
7280 //context->eventlock->lock();
7281
7282 // Pop event stack from front (this should be queue instead of stack?)
7283 context->event_stack = msg->next;
7284 free(msg);
7285 msg = NULL;
7286 }
7287
7288 // Unlock Stack
7289 context->eventlock->unlock();
7290 }
7291
7292 // Share CPU Time
7293 sleep_ms(10); //1 //sceKernelDelayThread(10000);
7294
7295 // Don't do anything if it's paused, otherwise the log will be flooded
7296 while (Core_IsStepping() && coreState != CORE_POWERDOWN && contexts != NULL && context->eventRunning) sleep_ms(10);
7297 }
7298
7299 // Process Last Messages
7300 if (contexts != NULL && context->event_stack != NULL)
7301 {
7302 // Claim Stack
7303 context->eventlock->lock();
7304
7305 // Iterate Message List
7306 int msg_count = 0;
7307 ThreadMessage * msg = context->event_stack;
7308 for (; msg != NULL; msg = msg->next)
7309 {
7310 // Default Optional Data
7311 void * opt = NULL;
7312
7313 // Grab Optional Data
7314 if (msg->optlen > 0) opt = ((u8 *)msg) + sizeof(ThreadMessage); //&msg[1]
7315
7316 INFO_LOG(SCENET, "EventLoop[%d]: Matching Event [EVENT=%d]\n", matchingId, msg->opcode);
7317
7318 //context->eventlock->unlock();
7319 // Original Call Event Handler
7320 //context->handler(context->id, msg->opcode, &msg->mac, msg->optlen, opt);
7321 // Notify Event Handlers
7322 notifyMatchingHandler(context, msg, opt, bufAddr, bufLen, args);
7323 //context->eventlock->lock();
7324 msg_count++;
7325 }
7326
7327 // Clear Event Message Stack
7328 clearStack(context, PSP_ADHOC_MATCHING_EVENT_STACK);
7329
7330 // Free Stack
7331 context->eventlock->unlock();
7332 INFO_LOG(SCENET, "EventLoop[%d]: Finished (%d msg)", matchingId, msg_count);
7333 }
7334
7335 // Free memory
7336 //if (Memory::IsValidAddress(bufAddr)) userMemory.Free(bufAddr);
7337
7338 // Delete Pointer Reference (and notify caller about finished cleanup)
7339 //context->eventThread = NULL;
7340 }
7341
7342 // Log Shutdown
7343 INFO_LOG(SCENET, "EventLoop: End of EventLoop[%i] Thread", matchingId);
7344
7345 // Return Zero to shut up Compiler
7346 return 0;
7347 }
7348
7349 /**
7350 * Matching IO Handler Thread
7351 * @param args sizeof(SceNetAdhocMatchingContext *)
7352 * @param argp SceNetAdhocMatchingContext *
7353 * @return Exit Point is never reached...
7354 */
matchingInputThread(int matchingId)7355 int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is using sceNetAdhocPdpRecv & sceNetAdhocPdpSend functions so it might be better to run this on PSP thread instead of real thread
7356 {
7357 SetCurrentThreadName("MatchingInput");
7358 auto n = GetI18NCategory("Networking");
7359 // Multithreading Lock
7360 peerlock.lock();
7361 // Cast Context
7362 SceNetAdhocMatchingContext* context = findMatchingContext(matchingId);
7363 // Multithreading Unlock
7364 peerlock.unlock();
7365
7366 // Last Ping
7367 u64_le lastping = 0;
7368
7369 // Last Hello
7370 u64_le lasthello = 0;
7371
7372 u64_le now;
7373
7374 static SceNetEtherAddr sendermac;
7375 static u32_le senderport;
7376 static int rxbuflen;
7377
7378 // Log Startup
7379 INFO_LOG(SCENET, "InputLoop: Begin of InputLoop[%i] Thread", matchingId);
7380
7381 // Run while needed...
7382 if (context != NULL) {
7383 while (contexts != NULL && context->inputRunning)
7384 {
7385 // Multithreading Lock
7386 peerlock.lock();
7387 // Cast Context
7388 context = findMatchingContext(matchingId);
7389 // Multithreading Unlock
7390 peerlock.unlock();
7391
7392 if (context != NULL) {
7393 now = CoreTiming::GetGlobalTimeUsScaled(); //time_now_d()*1000000.0;
7394
7395 // Hello Message Sending Context with unoccupied Slots
7396 if ((context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && (countChildren(context) < (context->maxpeers - 1))) || (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && findP2P(context) == NULL))
7397 {
7398 // Hello Message Broadcast necessary because of Hello Interval
7399 if (context->hello_int > 0)
7400 if (static_cast<s64>(now - lasthello) >= static_cast<s64>(context->hello_int))
7401 {
7402 // Broadcast Hello Message
7403 broadcastHelloMessage(context);
7404
7405 // Update Hello Timer
7406 lasthello = now;
7407 }
7408 }
7409
7410 // Ping Required
7411 if (context->keepalive_int > 0)
7412 if (static_cast<s64>(now - lastping) >= static_cast<s64>(context->keepalive_int))
7413 {
7414 // Broadcast Ping Message
7415 broadcastPingMessage(context);
7416
7417 // Update Ping Timer
7418 lastping = now;
7419 }
7420
7421 // Messages on Stack ready for processing
7422 if (context->input_stack != NULL)
7423 {
7424 // Claim Stack
7425 context->inputlock->lock();
7426
7427 // Iterate Message List
7428 ThreadMessage* msg = context->input_stack;
7429 while (msg != NULL)
7430 {
7431 // Default Optional Data
7432 void* opt = NULL;
7433
7434 // Grab Optional Data
7435 if (msg->optlen > 0) opt = ((u8*)msg) + sizeof(ThreadMessage);
7436
7437 //context->inputlock->unlock(); // Unlock to prevent race condition when locking peerlock
7438
7439 // Send Accept Packet
7440 if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_ACCEPT) sendAcceptPacket(context, &msg->mac, msg->optlen, opt);
7441
7442 // Send Join Packet
7443 else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_JOIN) sendJoinPacket(context, &msg->mac, msg->optlen, opt);
7444
7445 // Send Cancel Packet
7446 else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_CANCEL) sendCancelPacket(context, &msg->mac, msg->optlen, opt);
7447
7448 // Send Bulk Data Packet
7449 else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK) sendBulkDataPacket(context, &msg->mac, msg->optlen, opt);
7450
7451 // Send Birth Packet
7452 else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BIRTH) sendBirthPacket(context, &msg->mac);
7453
7454 // Send Death Packet
7455 else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_DEATH) sendDeathPacket(context, &msg->mac);
7456
7457 // Cancel Bulk Data Transfer (does nothing as of now as we fire and forget anyway) // Do we need to check DeathPacket and ByePacket here?
7458 //else if(msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK_ABORT) sendAbortBulkDataPacket(context, &msg->mac, msg->optlen, opt);
7459
7460 //context->inputlock->lock(); // Lock again
7461
7462 // Pop input stack from front (this should be queue instead of stack?)
7463 context->input_stack = msg->next;
7464 free(msg);
7465 msg = context->input_stack;
7466 }
7467
7468 // Free Stack
7469 context->inputlock->unlock();
7470 }
7471
7472 // Receive PDP Datagram
7473 // FIXME: When using JPCSP + prx files, the "SceNetAdhocMatchingInput" thread is using blocking PdpRecv with infinite(0) timeout, which can be stopped/aborted using SetSocketAlert, while "SceNetAdhocMatchingEvent" thread is using non-blocking for sending
7474 rxbuflen = context->rxbuflen;
7475 senderport = 0;
7476 // Lock the peer first before locking the socket to avoid race condiion
7477 peerlock.lock();
7478 context->socketlock->lock();
7479 int recvresult = sceNetAdhocPdpRecv(context->socket, &sendermac, &senderport, context->rxbuf, &rxbuflen, 0, ADHOC_F_NONBLOCK);
7480 context->socketlock->unlock();
7481 peerlock.unlock();
7482
7483 // Received Data from a Sender that interests us
7484 // Note: There are cases where the sender port might be re-mapped by router or ISP, so we shouldn't check the source port.
7485 if (recvresult == 0 && rxbuflen > 0)
7486 {
7487 // Log Receive Success
7488 if (context->rxbuf[0] > 1) {
7489 INFO_LOG(SCENET, "InputLoop[%d]: Received %d Bytes (Opcode[%d]=%s)", matchingId, rxbuflen, context->rxbuf[0], getMatchingOpcodeStr(context->rxbuf[0]));
7490 }
7491
7492 // Update Peer Timestamp
7493 peerlock.lock();
7494 SceNetAdhocctlPeerInfo* peer = findFriend(&sendermac);
7495 if (peer != NULL) {
7496 now = CoreTiming::GetGlobalTimeUsScaled();
7497 s64 delta = now - peer->last_recv;
7498 DEBUG_LOG(SCENET, "Timestamp LastRecv Delta: %lld (%llu - %llu) from %s", delta, now, peer->last_recv, mac2str(&sendermac).c_str());
7499 if (peer->last_recv != 0) peer->last_recv = std::max(peer->last_recv, now - defaultLastRecvDelta);
7500 }
7501 else {
7502 WARN_LOG(SCENET, "InputLoop[%d]: Unknown Peer[%s:%u] (Recved=%i, Length=%i)", matchingId, mac2str(&sendermac).c_str(), senderport, recvresult, rxbuflen);
7503 }
7504
7505 // Show a warning if other player is having their port being re-mapped, thus that other player may have issue with the communication.
7506 // Note: That other player may need to switch side between host and join, or reboot their router to solve this issue.
7507 if (context->port != senderport && senderport != (*context->peerPort)[sendermac]) {
7508 char name[9] = {};
7509 if (peer != NULL)
7510 truncate_cpy(name, sizeof(name), (const char*)peer->nickname.data);
7511 WARN_LOG(SCENET, "InputLoop[%d]: Unknown Source Port from [%s][%s:%u -> %u] (Recved=%i, Length=%i)", matchingId, name, mac2str(&sendermac).c_str(), senderport, context->port, recvresult, rxbuflen);
7512 host->NotifyUserMessage(std::string(n->T("AM: Data from Unknown Port")) + std::string(" [") + std::string(name) + std::string("]:") + std::to_string(senderport) + std::string(" -> ") + std::to_string(context->port) + std::string(" (") + std::to_string(portOffset) + std::string(")"), 2.0, 0x0080ff);
7513 }
7514 // Keep tracks of re-mapped peer's ports for further communication.
7515 // Note: This will only works if this player were able to receives data on normal port from other players (ie. this player's port wasn't remapped)
7516 (*context->peerPort)[sendermac] = senderport;
7517 peerlock.unlock();
7518
7519 // Ping Packet
7520 if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_PING) actOnPingPacket(context, &sendermac);
7521
7522 // Hello Packet
7523 else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_HELLO) actOnHelloPacket(context, &sendermac, rxbuflen);
7524
7525 // Join Packet
7526 else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_JOIN) actOnJoinPacket(context, &sendermac, rxbuflen);
7527
7528 // Accept Packet
7529 else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_ACCEPT) actOnAcceptPacket(context, &sendermac, rxbuflen);
7530
7531 // Cancel Packet
7532 else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_CANCEL) actOnCancelPacket(context, &sendermac, rxbuflen);
7533
7534 // Bulk Data Packet
7535 else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BULK) actOnBulkDataPacket(context, &sendermac, rxbuflen);
7536
7537 // Abort Bulk Data Packet
7538 //else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BULK_ABORT) actOnAbortBulkDataPacket(context, &sendermac, rxbuflen);
7539
7540 // Birth Packet
7541 else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BIRTH) actOnBirthPacket(context, &sendermac, rxbuflen);
7542
7543 // Death Packet
7544 else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_DEATH) actOnDeathPacket(context, &sendermac, rxbuflen);
7545
7546 // Bye Packet
7547 else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BYE) actOnByePacket(context, &sendermac);
7548
7549 // Ignore Incoming Trash Data
7550 }
7551
7552 // Handle Peer Timeouts
7553 handleTimeout(context);
7554 }
7555 // Share CPU Time
7556 sleep_ms(10); //1 //sceKernelDelayThread(10000);
7557
7558 // Don't do anything if it's paused, otherwise the log will be flooded
7559 while (Core_IsStepping() && coreState != CORE_POWERDOWN && contexts != NULL && context->inputRunning) sleep_ms(10);
7560 }
7561
7562 if (contexts != NULL) {
7563 // Process Last Messages
7564 if (context->input_stack != NULL)
7565 {
7566 // Claim Stack
7567 context->inputlock->lock();
7568
7569 // Iterate Message List
7570 int msg_count = 0;
7571 ThreadMessage* msg = context->input_stack;
7572 while (msg != NULL)
7573 {
7574 // Default Optional Data
7575 void* opt = NULL;
7576
7577 // Grab Optional Data
7578 if (msg->optlen > 0) opt = ((u8*)msg) + sizeof(ThreadMessage);
7579
7580 // Send Accept Packet
7581 if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_ACCEPT) sendAcceptPacket(context, &msg->mac, msg->optlen, opt);
7582
7583 // Send Join Packet
7584 else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_JOIN) sendJoinPacket(context, &msg->mac, msg->optlen, opt);
7585
7586 // Send Cancel Packet
7587 else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_CANCEL) sendCancelPacket(context, &msg->mac, msg->optlen, opt);
7588
7589 // Send Bulk Data Packet
7590 else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK) sendBulkDataPacket(context, &msg->mac, msg->optlen, opt);
7591
7592 // Send Birth Packet
7593 else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BIRTH) sendBirthPacket(context, &msg->mac);
7594
7595 // Send Death Packet
7596 else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_DEATH) sendDeathPacket(context, &msg->mac);
7597
7598 // Cancel Bulk Data Transfer (does nothing as of now as we fire and forget anyway) // Do we need to check DeathPacket and ByePacket here?
7599 //else if(msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK_ABORT) sendAbortBulkDataPacket(context, &msg->mac, msg->optlen, opt);
7600
7601 // Pop input stack from front (this should be queue instead of stack?)
7602 context->input_stack = msg->next;
7603 free(msg);
7604 msg = context->input_stack;
7605 msg_count++;
7606 }
7607
7608 // Free Stack
7609 context->inputlock->unlock();
7610 INFO_LOG(SCENET, "InputLoop[%d]: Finished (%d msg)", matchingId, msg_count);
7611 }
7612
7613 // Clear IO Message Stack
7614 clearStack(context, PSP_ADHOC_MATCHING_INPUT_STACK);
7615
7616 // Send Bye Messages. FIXME: Official prx seems to be sending DEATH instead of BYE packet during MatchingStop? But DEATH packet doesn't works with DBZ Team Tag
7617 sendByePacket(context);
7618
7619 // Free Peer List Buffer
7620 clearPeerList(context); //deleteAllMembers(context);
7621
7622 // Delete Pointer Reference (and notify caller about finished cleanup)
7623 //context->inputThread = NULL;
7624 }
7625 }
7626
7627 // Log Shutdown
7628 INFO_LOG(SCENET, "InputLoop: End of InputLoop[%i] Thread", matchingId);
7629
7630 // Terminate Thread
7631 //sceKernelExitDeleteThread(0);
7632
7633 // Return Zero to shut up Compiler
7634 return 0;
7635 }
7636