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(&parameter.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,&parameter);
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(&parameter.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(&parameter.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 *)&parameter.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