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 
19 // proAdhoc
20 
21 // This is a direct port of Coldbird's code from http://code.google.com/p/aemu/
22 // All credit goes to him!
23 
24 #include "ppsspp_config.h"
25 
26 #if defined(_WIN32)
27 #include <WinSock2.h>
28 #include "Common/CommonWindows.h"
29 #endif
30 
31 #if !defined(_WIN32)
32 #include <unistd.h>
33 #include <netinet/tcp.h>
34 #include <sys/ioctl.h>
35 #include <sys/socket.h>
36 #include <sys/types.h>
37 #if !PPSSPP_PLATFORM(SWITCH)
38 #include <ifaddrs.h>
39 #endif // !PPSSPP_PLATFORM(SWITCH)
40 #endif
41 
42 #ifndef MSG_NOSIGNAL
43 // Default value to 0x00 (do nothing) in systems where it's not supported.
44 #define MSG_NOSIGNAL 0x00
45 #endif
46 
47 #if defined(HAVE_LIBNX) || PPSSPP_PLATFORM(SWITCH)
48 #undef __BSD_VISIBLE
49 #define __BSD_VISIBLE 1
50 #include <switch.h>
51 #define TCP_MAXSEG 2
52 #endif // defined(HAVE_LIBNX) || PPSSPP_PLATFORM(SWITCH)
53 
54 #include <mutex>
55 #include <cstring>
56 
57 #include "Common/Data/Text/I18n.h"
58 #include "Common/Thread/ThreadUtil.h"
59 #include "Common/Data/Text/Parsers.h"
60 
61 #include "Common/Serialize/SerializeFuncs.h"
62 #include "Common/TimeUtil.h"
63 #include "Core/Core.h"
64 #include "Core/Host.h"
65 #include "Core/HLE/sceKernelInterrupt.h"
66 #include "Core/HLE/sceKernelThread.h"
67 #include "Core/HLE/sceKernelMemory.h"
68 #include "Core/HLE/sceNetAdhoc.h"
69 #include "Core/Instance.h"
70 #include "proAdhoc.h"
71 
72 #if PPSSPP_PLATFORM(SWITCH) && !defined(INADDR_NONE)
73 // Missing toolchain define
74 #define INADDR_NONE 0xFFFFFFFF
75 #endif
76 
77 uint16_t portOffset;
78 uint32_t minSocketTimeoutUS;
79 uint32_t fakePoolSize                 = 0;
80 SceNetAdhocMatchingContext * contexts = NULL;
81 char* dummyPeekBuf64k                 = NULL;
82 int dummyPeekBuf64kSize               = 65536;
83 int one                               = 1;
84 std::atomic<bool> friendFinderRunning(false);
85 SceNetAdhocctlPeerInfo * friends      = NULL;
86 SceNetAdhocctlScanInfo * networks     = NULL;
87 SceNetAdhocctlScanInfo * newnetworks  = NULL;
88 u64 adhocctlStartTime                 = 0;
89 bool isAdhocctlNeedLogin              = false;
90 bool isAdhocctlBusy                   = false;
91 int adhocctlState                     = ADHOCCTL_STATE_DISCONNECTED;
92 int adhocctlCurrentMode               = ADHOCCTL_MODE_NONE;
93 int adhocConnectionType               = ADHOC_CONNECT;
94 
95 int gameModeSocket                    = (int)INVALID_SOCKET; // UDP/PDP socket? on Master only?
96 int gameModeBuffSize                  = 0;
97 u8* gameModeBuffer                    = nullptr;
98 GameModeArea masterGameModeArea;
99 std::vector<GameModeArea> replicaGameModeAreas;
100 std::vector<SceNetEtherAddr> requiredGameModeMacs;
101 std::vector<SceNetEtherAddr> gameModeMacs;
102 std::map<SceNetEtherAddr, u16_le> gameModePeerPorts;
103 
104 int actionAfterAdhocMipsCall;
105 int actionAfterMatchingMipsCall;
106 
107 // Broadcast MAC
108 uint8_t broadcastMAC[ETHER_ADDR_LEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
109 
110 std::atomic<int> metasocket((int)INVALID_SOCKET);
111 SceNetAdhocctlParameter parameter;
112 SceNetAdhocctlAdhocId product_code;
113 std::thread friendFinderThread;
114 std::recursive_mutex peerlock;
115 AdhocSocket* adhocSockets[MAX_SOCKET];
116 bool isOriPort = false;
117 bool isLocalServer = false;
118 SockAddrIN4 g_adhocServerIP;
119 SockAddrIN4 g_localhostIP;
120 sockaddr LocalIP;
121 int defaultWlanChannel = PSP_SYSTEMPARAM_ADHOC_CHANNEL_11; // Don't put 0(Auto) here, it needed to be a valid/actual channel number
122 
123 static std::mutex chatLogLock;
124 static std::vector<std::string> chatLog;
125 static int chatMessageGeneration = 0;
126 static int chatMessageCount = 0;
127 
isMacMatch(const SceNetEtherAddr * addr1,const SceNetEtherAddr * addr2)128 bool isMacMatch(const SceNetEtherAddr* addr1, const SceNetEtherAddr* addr2) {
129 	// Ignoring the 1st byte since there are games (ie. Gran Turismo) who tamper with the 1st byte of OUI to change the unicast/multicast bit
130 	return (memcmp(((const char*)addr1)+1, ((const char*)addr2)+1, ETHER_ADDR_LEN-1) == 0);
131 }
132 
isLocalMAC(const SceNetEtherAddr * addr)133 bool isLocalMAC(const SceNetEtherAddr * addr) {
134 	SceNetEtherAddr saddr;
135 	getLocalMac(&saddr);
136 
137 	return isMacMatch(addr, &saddr);
138 }
139 
isPDPPortInUse(uint16_t port)140 bool isPDPPortInUse(uint16_t port) {
141 	// Iterate Elements
142 	for (int i = 0; i < MAX_SOCKET; i++) {
143 		auto sock = adhocSockets[i];
144 		if (sock != NULL && sock->type == SOCK_PDP)
145 			if (sock->data.pdp.lport == port)
146 				return true;
147 	}
148 	// Unused Port
149 	return false;
150 }
151 
isPTPPortInUse(uint16_t port,bool forListen,SceNetEtherAddr * dstmac,uint16_t dstport)152 bool isPTPPortInUse(uint16_t port, bool forListen, SceNetEtherAddr* dstmac, uint16_t dstport) {
153 	// Iterate Sockets
154 	for (int i = 0; i < MAX_SOCKET; i++) {
155 		auto sock = adhocSockets[i];
156 		if (sock != NULL && sock->type == SOCK_PTP)
157 			// 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 (unless destination mac or port are different).
158 			if (sock->data.ptp.lport == port &&
159 			    ((forListen && sock->data.ptp.state == ADHOC_PTP_STATE_LISTEN) ||
160 			     (!forListen && sock->data.ptp.state != ADHOC_PTP_STATE_LISTEN &&
161 			      sock->data.ptp.pport == dstport && dstmac != nullptr && isMacMatch(&sock->data.ptp.paddr, dstmac))))
162 			{
163 				return true;
164 			}
165 	}
166 	// Unused Port
167 	return false;
168 }
169 
170 // Replacement for inet_ntoa since it's getting deprecated
ip2str(in_addr in)171 std::string ip2str(in_addr in) {
172 	char str[INET_ADDRSTRLEN] = "...";
173 	u8* ipptr = (u8*)&in;
174 	snprintf(str, sizeof(str), "%u.%u.%u.%u", ipptr[0], ipptr[1], ipptr[2], ipptr[3]);
175 	return std::string(str);
176 }
177 
mac2str(SceNetEtherAddr * mac)178 std::string mac2str(SceNetEtherAddr* mac) {
179 	char str[18] = ":::::";
180 
181 	if (mac != NULL) {
182 		snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x", mac->data[0], mac->data[1], mac->data[2], mac->data[3], mac->data[4], mac->data[5]);
183 	}
184 
185 	return std::string(str);
186 }
187 
addMember(SceNetAdhocMatchingContext * context,SceNetEtherAddr * mac)188 SceNetAdhocMatchingMemberInternal* addMember(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac) {
189 	if (context == NULL || mac == NULL) return NULL;
190 
191 	SceNetAdhocMatchingMemberInternal * peer = findPeer(context, mac);
192 	// Already existed
193 	if (peer != NULL) {
194 		WARN_LOG(SCENET, "Member Peer Already Existed! Updating [%s]", mac2str(mac).c_str());
195 		peer->state = 0;
196 		peer->sending = 0;
197 		peer->lastping = CoreTiming::GetGlobalTimeUsScaled();
198 	}
199 	// Member is not added yet
200 	else {
201 		peer = (SceNetAdhocMatchingMemberInternal *)malloc(sizeof(SceNetAdhocMatchingMemberInternal));
202 		if (peer != NULL) {
203 			memset(peer, 0, sizeof(SceNetAdhocMatchingMemberInternal));
204 			peer->mac = *mac;
205 			peer->lastping = CoreTiming::GetGlobalTimeUsScaled();
206 			peerlock.lock();
207 			peer->next = context->peerlist;
208 			context->peerlist = peer;
209 			peerlock.unlock();
210 		}
211 	}
212 	return peer;
213 }
214 
addFriend(SceNetAdhocctlConnectPacketS2C * packet)215 void addFriend(SceNetAdhocctlConnectPacketS2C * packet) {
216 	if (packet == NULL) return;
217 
218 	// Multithreading Lock
219 	std::lock_guard<std::recursive_mutex> guard(peerlock);
220 
221 	SceNetAdhocctlPeerInfo * peer = findFriend(&packet->mac);
222 	// Already existed
223 	if (peer != NULL) {
224 		u32 tmpip = packet->ip;
225 		WARN_LOG(SCENET, "Friend Peer Already Existed! Updating [%s][%s][%s]", mac2str(&packet->mac).c_str(), ip2str(*(struct in_addr*)&tmpip).c_str(), packet->name.data); //inet_ntoa(*(in_addr*)&packet->ip)
226 		peer->nickname = packet->name;
227 		peer->mac_addr = packet->mac;
228 		peer->ip_addr = packet->ip;
229 		// Update TimeStamp
230 		peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
231 	}
232 	else {
233 		// Allocate Structure
234 		peer = (SceNetAdhocctlPeerInfo *)malloc(sizeof(SceNetAdhocctlPeerInfo));
235 		// Allocated Structure
236 		if (peer != NULL) {
237 			// Clear Memory
238 			memset(peer, 0, sizeof(SceNetAdhocctlPeerInfo));
239 
240 			// Save Nickname
241 			peer->nickname = packet->name;
242 
243 			// Save MAC Address
244 			peer->mac_addr = packet->mac;
245 
246 			// Save IP Address
247 			peer->ip_addr = packet->ip;
248 
249 			// TimeStamp
250 			peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
251 
252 			// Link to existing Peers
253 			peer->next = friends;
254 
255 			// Link into Peerlist
256 			friends = peer;
257 		}
258 	}
259 }
260 
findFriend(SceNetEtherAddr * MAC)261 SceNetAdhocctlPeerInfo * findFriend(SceNetEtherAddr * MAC) {
262 	if (MAC == NULL) return NULL;
263 
264 	// Friends Reference
265 	SceNetAdhocctlPeerInfo * peer = friends;
266 
267 	// Iterate Friends
268 	for (; peer != NULL; peer = peer->next) {
269 		if (isMacMatch(&peer->mac_addr, MAC)) break;
270 	}
271 
272 	// Return found friend
273 	return peer;
274 }
275 
findFriendByIP(uint32_t ip)276 SceNetAdhocctlPeerInfo* findFriendByIP(uint32_t ip) {
277 	// Friends Reference
278 	SceNetAdhocctlPeerInfo* peer = friends;
279 
280 	// Iterate Friends
281 	for (; peer != NULL; peer = peer->next) {
282 		if (peer->ip_addr == ip) break;
283 	}
284 
285 	// Return found friend
286 	return peer;
287 }
288 
IsSocketReady(int fd,bool readfd,bool writefd,int * errorcode,int timeoutUS)289 int IsSocketReady(int fd, bool readfd, bool writefd, int* errorcode, int timeoutUS) {
290 	fd_set readfds, writefds;
291 	timeval tval;
292 
293 	// Avoid getting Fatal signal 6 (SIGABRT) on linux/android
294 	if (fd < 0)
295 	    return SOCKET_ERROR;
296 
297 	FD_ZERO(&readfds);
298 	writefds = readfds;
299 	if (readfd) {
300 		FD_SET(fd, &readfds);
301 	}
302 	if (writefd) {
303 		FD_SET(fd, &writefds);
304 	}
305 	tval.tv_sec = timeoutUS / 1000000;
306 	tval.tv_usec = timeoutUS % 1000000;
307 
308 	int ret = select(fd + 1, readfd? &readfds: nullptr, writefd? &writefds: nullptr, nullptr, &tval);
309 	if (errorcode != nullptr)
310 		*errorcode = errno;
311 
312 	return ret;
313 }
314 
changeBlockingMode(int fd,int nonblocking)315 void changeBlockingMode(int fd, int nonblocking) {
316 	unsigned long on = 1;
317 	unsigned long off = 0;
318 #if defined(_WIN32)
319 	if (nonblocking) {
320 		// Change to Non-Blocking Mode
321 		ioctlsocket(fd, FIONBIO, &on);
322 	}
323 	else {
324 		// Change to Blocking Mode
325 		ioctlsocket(fd, FIONBIO, &off);
326 	}
327 // If they have O_NONBLOCK, use the POSIX way to do it. On POSIX sockets Error code would be EINPROGRESS instead of EAGAIN
328 //#elif defined(O_NONBLOCK)
329 #else
330 	int flags = fcntl(fd, F_GETFL, 0);
331 	// Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5.
332 	if (flags == -1)
333 		flags = 0;
334 	if (nonblocking) {
335 		// Set Non-Blocking Flag
336 		fcntl(fd, F_SETFL, flags | O_NONBLOCK);
337 	}
338 	else {
339 		// Remove Non-Blocking Flag
340 		fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
341 	}
342 // Otherwise, use the old way of doing it (UNIX way). On UNIX sockets Error code would be EAGAIN instead of EINPROGRESS
343 /*#else
344 	if (nonblocking) {
345 		// Change to Non - Blocking Mode
346 		ioctl(fd, FIONBIO, (char*)&on);
347 	}
348 	else {
349 		// Change to Blocking Mode
350 		ioctl(fd, FIONBIO, (char*)&off);
351 	}*/
352 #endif
353 }
354 
countAvailableNetworks(const bool excludeSelf)355 int countAvailableNetworks(const bool excludeSelf) {
356 	// Network Count
357 	int count = 0;
358 
359 	// Group Reference
360 	SceNetAdhocctlScanInfo * group = networks;
361 
362 	// Count Groups
363 	for (; group != NULL && (!excludeSelf || !isLocalMAC(&group->bssid.mac_addr)); group = group->next) count++;
364 
365 	// Return Network Count
366 	return count;
367 }
368 
findGroup(SceNetEtherAddr * MAC)369 SceNetAdhocctlScanInfo * findGroup(SceNetEtherAddr * MAC) {
370 	if (MAC == NULL) return NULL;
371 
372 	// Groups Reference
373 	SceNetAdhocctlScanInfo * group = networks;
374 
375 	// Iterate Groups
376 	for (; group != NULL; group = group->next) {
377 		if (isMacMatch(&group->bssid.mac_addr, MAC)) break;
378 	}
379 
380 	// Return found group
381 	return group;
382 }
383 
freeGroupsRecursive(SceNetAdhocctlScanInfo * node)384 void freeGroupsRecursive(SceNetAdhocctlScanInfo * node) {
385 	// End of List
386 	if (node == NULL) return;
387 
388 	// Increase Recursion Depth
389 	freeGroupsRecursive(node->next);
390 
391 	// Free Memory
392 	free(node);
393 	node = NULL;
394 }
395 
deleteAllAdhocSockets()396 void deleteAllAdhocSockets() {
397 	// Iterate Element
398 	for (int i = 0; i < MAX_SOCKET; i++) {
399 		// Active Socket
400 		if (adhocSockets[i] != NULL) {
401 			auto sock = adhocSockets[i];
402 			int fd = -1;
403 
404 			if (sock->type == SOCK_PTP)
405 				fd = sock->data.ptp.id;
406 			else if (sock->type == SOCK_PDP)
407 				fd = sock->data.pdp.id;
408 
409 			if (fd > 0) {
410 				// Close Socket
411 				shutdown(fd, SD_BOTH);
412 				closesocket(fd);
413 			}
414 			// Free Memory
415 			free(adhocSockets[i]);
416 
417 			// Delete Reference
418 			adhocSockets[i] = NULL;
419 		}
420 	}
421 }
422 
deleteAllGMB()423 void deleteAllGMB() {
424 	if (gameModeBuffer) {
425 		free(gameModeBuffer);
426 		gameModeBuffer = nullptr;
427 		gameModeBuffSize = 0;
428 	}
429 	if (masterGameModeArea.data) {
430 		free(masterGameModeArea.data);
431 		masterGameModeArea = { 0 };
432 	}
433 	for (auto& it : replicaGameModeAreas) {
434 		if (it.data) {
435 			free(it.data);
436 			it.data = nullptr;
437 		}
438 	}
439 	replicaGameModeAreas.clear();
440 	gameModeMacs.clear();
441 	requiredGameModeMacs.clear();
442 }
443 
deleteFriendByIP(uint32_t ip)444 void deleteFriendByIP(uint32_t ip) {
445 	// Previous Peer Reference
446 	SceNetAdhocctlPeerInfo * prev = NULL;
447 
448 	// Peer Pointer
449 	SceNetAdhocctlPeerInfo * peer = friends;
450 
451 	// Iterate Peers
452 	for (; peer != NULL; peer = peer->next) {
453 		// Found Peer
454 		if (peer->ip_addr == ip) {
455 
456 			// Multithreading Lock
457 			peerlock.lock();
458 
459 			// Unlink Left (Beginning)
460 			/*if (prev == NULL) friends = peer->next;
461 
462 			// Unlink Left (Other)
463 			else prev->next = peer->next;
464 			*/
465 
466 			u32 tmpip = peer->ip_addr;
467 			INFO_LOG(SCENET, "Removing Friend Peer %s [%s]", mac2str(&peer->mac_addr).c_str(), ip2str(*(struct in_addr *)&tmpip).c_str()); //inet_ntoa(*(in_addr*)&peer->ip_addr)
468 
469 			// Free Memory
470 			//free(peer);
471 			//peer = NULL;
472 			// Instead of removing it from the list we'll make it timed out since most Matching games are moving group and may still need the peer data thus not recognizing it as Unknown peer
473 			peer->last_recv = 0; //CoreTiming::GetGlobalTimeUsScaled();
474 
475 			// Multithreading Unlock
476 			peerlock.unlock();
477 
478 			// Stop Search
479 			break;
480 		}
481 
482 		// Set Previous Reference
483 		prev = peer;
484 	}
485 }
486 
findFreeMatchingID()487 int findFreeMatchingID() {
488 	// Minimum Matching ID
489 	int min = 1;
490 
491 	// Maximum Matching ID
492 	int max = 0;
493 
494 	// Find highest Matching ID
495 	SceNetAdhocMatchingContext * item = contexts;
496 	for (; item != NULL; item = item->next) {
497 		// New Maximum
498 		if (max < item->id) max = item->id;
499 	}
500 
501 	// Find unoccupied ID
502 	int i = min;
503 	for (; i < max; i++) {
504 		// Found unoccupied ID
505 		if (findMatchingContext(i) == NULL) return i;
506 	}
507 
508 	// Append at virtual end
509 	return max + 1;
510 }
511 
findMatchingContext(int id)512 SceNetAdhocMatchingContext * findMatchingContext(int id) {
513 	// Iterate Matching Context List
514 	SceNetAdhocMatchingContext * item = contexts;
515 	for (; item != NULL; item = item->next) { // Found Matching ID
516 		if (item->id == id) return item;
517 	}
518 
519 	// Context not found
520 	return NULL;
521 }
522 
523 /**
524 * Find Outgoing Request Target Peer
525 * @param context Matching Context Pointer
526 * @return Internal Peer Reference or... NULL
527 */
findOutgoingRequest(SceNetAdhocMatchingContext * context)528 SceNetAdhocMatchingMemberInternal * findOutgoingRequest(SceNetAdhocMatchingContext * context)
529 {
530 	// Iterate Peer List for Matching Target
531 	SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
532 	for (; peer != NULL; peer = peer->next)
533 	{
534 		// Found Peer in List
535 		if (peer->state == PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST) return peer;
536 	}
537 
538 	// Peer not found
539 	return NULL;
540 }
541 
542 /**
543 * Remove unneeded Peer Data after being accepted to a match
544 * @param context Matching Context Pointer
545 */
postAcceptCleanPeerList(SceNetAdhocMatchingContext * context)546 void postAcceptCleanPeerList(SceNetAdhocMatchingContext * context)
547 {
548 	int delcount = 0;
549 	int peercount = 0;
550 	// Acquire Peer Lock
551 	peerlock.lock();
552 
553 	// Iterate Peer List
554 	SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
555 	while (peer != NULL)
556 	{
557 		// Save next Peer just in case we have to delete this one
558 		SceNetAdhocMatchingMemberInternal * next = peer->next;
559 
560 		// Unneeded Peer
561 		if (peer->state != PSP_ADHOC_MATCHING_PEER_CHILD && peer->state != PSP_ADHOC_MATCHING_PEER_P2P && peer->state != PSP_ADHOC_MATCHING_PEER_PARENT && peer->state != 0) {
562 			deletePeer(context, peer);
563 			delcount++;
564 		}
565 
566 		// Move to Next Peer
567 		peer = next;
568 		peercount++;
569 	}
570 
571 	// Free Peer Lock
572 	peerlock.unlock();
573 
574 	INFO_LOG(SCENET, "Removing Unneeded Peers (%i/%i)", delcount, peercount);
575 }
576 
577 /**
578 * Add Sibling-Data that was sent with Accept-Datagram
579 * @param context Matching Context Pointer
580 * @param siblingcount Number of Siblings
581 * @param siblings Sibling MAC Array
582 */
postAcceptAddSiblings(SceNetAdhocMatchingContext * context,int siblingcount,SceNetEtherAddr * siblings)583 void postAcceptAddSiblings(SceNetAdhocMatchingContext * context, int siblingcount, SceNetEtherAddr * siblings)
584 {
585 	// Cast Sibling MAC Array to uint8_t
586 	// PSP CPU has a problem with non-4-byte aligned Pointer Access.
587 	// As the buffer of "siblings" isn't properly aligned I don't want to risk a crash.
588 	uint8_t * siblings_u8 = (uint8_t *)siblings;
589 
590 	peerlock.lock();
591 	// Iterate Siblings. Reversed so these siblings are added into peerlist in the same order with the peerlist on host/parent side
592 	for (int i = siblingcount - 1; i >= 0 ; i--)
593 	{
594 		SceNetEtherAddr* mac = (SceNetEtherAddr*)(siblings_u8 + sizeof(SceNetEtherAddr) * i);
595 
596 		auto peer = findPeer(context, mac);
597 		// Already exist
598 		if (peer != NULL) {
599 			// Set Peer State
600 			peer->state = PSP_ADHOC_MATCHING_PEER_CHILD;
601 			peer->sending = 0;
602 			peer->lastping = CoreTiming::GetGlobalTimeUsScaled();
603 			WARN_LOG(SCENET, "Updating Sibling Peer %s", mac2str(mac).c_str());
604 		}
605 		else {
606 			// Allocate Memory
607 			SceNetAdhocMatchingMemberInternal* sibling = (SceNetAdhocMatchingMemberInternal*)malloc(sizeof(SceNetAdhocMatchingMemberInternal));
608 
609 			// Allocated Memory
610 			if (sibling != NULL)
611 			{
612 				// Clear Memory
613 				memset(sibling, 0, sizeof(SceNetAdhocMatchingMemberInternal));
614 
615 				// Save MAC Address
616 				memcpy(&sibling->mac, mac, sizeof(SceNetEtherAddr));
617 
618 				// Set Peer State
619 				sibling->state = PSP_ADHOC_MATCHING_PEER_CHILD;
620 
621 				// Initialize Ping Timer
622 				sibling->lastping = CoreTiming::GetGlobalTimeUsScaled(); //time_now_d()*1000000.0;
623 
624 				// Link Peer
625 				sibling->next = context->peerlist;
626 				context->peerlist = sibling;
627 
628 				// Spawn Established Event. FIXME: ESTABLISHED event should only be triggered for Parent/P2P peer?
629 				//spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_ESTABLISHED, &sibling->mac, 0, NULL);
630 
631 				INFO_LOG(SCENET, "Accepting Sibling Peer %s", mac2str(&sibling->mac).c_str());
632 			}
633 		}
634 	}
635 	peerlock.unlock();
636 }
637 
638 /**
639 * Count Children Peers (for Parent)
640 * @param context Matching Context Pointer
641 * @return Number of Children
642 */
countChildren(SceNetAdhocMatchingContext * context,const bool excludeTimedout)643 s32_le countChildren(SceNetAdhocMatchingContext * context, const bool excludeTimedout)
644 {
645 	// Children Counter
646 	s32_le count = 0;
647 
648 	// Iterate Peer List for Matching Target
649 	SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
650 	for (; peer != NULL; peer = peer->next)
651 	{
652 		// Exclude timedout members?
653 		if (!excludeTimedout || peer->lastping != 0)
654 		// Increase Children Counter
655 		if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) count++;
656 	}
657 
658 	// Return Children Count
659 	return count;
660 }
661 
662 /**
663 * Find Peer in Context by MAC
664 * @param context Matching Context Pointer
665 * @param mac Peer MAC Address
666 * @return Internal Peer Reference or... NULL
667 */
findPeer(SceNetAdhocMatchingContext * context,SceNetEtherAddr * mac)668 SceNetAdhocMatchingMemberInternal * findPeer(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac)
669 {
670 	if (mac == NULL)
671 		return NULL;
672 
673 	// Iterate Peer List for Matching Target
674 	SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
675 	for (; peer != NULL; peer = peer->next)
676 	{
677 		// Found Peer in List
678 		if (isMacMatch(&peer->mac, mac))
679 		{
680 			// Return Peer Pointer
681 			return peer;
682 		}
683 	}
684 
685 	// Peer not found
686 	return NULL;
687 }
688 
689 /**
690 * Find Parent Peer
691 * @param context Matching Context Pointer
692 * @return Internal Peer Reference or... NULL
693 */
findParent(SceNetAdhocMatchingContext * context)694 SceNetAdhocMatchingMemberInternal * findParent(SceNetAdhocMatchingContext * context)
695 {
696 	// Iterate Peer List for Matching Target
697 	SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
698 	for (; peer != NULL; peer = peer->next)
699 	{
700 		// Found Peer in List
701 		if (peer->state == PSP_ADHOC_MATCHING_PEER_PARENT) return peer;
702 	}
703 
704 	// Peer not found
705 	return NULL;
706 }
707 
708 /**
709 * Find P2P Buddy Peer
710 * @param context Matching Context Pointer
711 * @return Internal Peer Reference or... NULL
712 */
findP2P(SceNetAdhocMatchingContext * context,const bool excludeTimedout)713 SceNetAdhocMatchingMemberInternal * findP2P(SceNetAdhocMatchingContext * context, const bool excludeTimedout)
714 {
715 	// Iterate Peer List for Matching Target
716 	SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
717 	for (; peer != NULL; peer = peer->next)
718 	{
719 		// Exclude timedout members?
720 		if (!excludeTimedout || peer->lastping != 0)
721 		// Found Peer in List
722 		if (peer->state == PSP_ADHOC_MATCHING_PEER_P2P) return peer;
723 	}
724 
725 	// Peer not found
726 	return NULL;
727 }
728 
729 /**
730 * Delete Peer from List
731 * @param context Matching Context Pointer
732 * @param peer Internal Peer Reference
733 */
deletePeer(SceNetAdhocMatchingContext * context,SceNetAdhocMatchingMemberInternal * & peer)734 void deletePeer(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal *& peer)
735 {
736 	// Valid Arguments
737 	if (context != NULL && peer != NULL)
738 	{
739 		peerlock.lock();
740 
741 		// Previous Peer Reference
742 		SceNetAdhocMatchingMemberInternal * previous = NULL;
743 
744 		// Iterate Peer List
745 		SceNetAdhocMatchingMemberInternal * item = context->peerlist;
746 		for (; item != NULL; item = item->next)
747 		{
748 			// Found Peer Match
749 			if (item == peer) break;
750 
751 			// Set Previous Peer
752 			previous = item;
753 		}
754 
755 		if (item != NULL) {
756 			// Middle Item
757 			if (previous != NULL) previous->next = item->next;
758 
759 			// Beginning Item
760 			else context->peerlist = item->next;
761 
762 			INFO_LOG(SCENET, "Removing Member Peer %s", mac2str(&peer->mac).c_str());
763 		}
764 
765 		// Free Peer Memory
766 		free(peer);
767 		peer = NULL;
768 
769 		peerlock.unlock();
770 	}
771 }
772 
773 /**
774 * Safely Link Thread Message to Event Thread Stack
775 * @param context Matching Context Pointer
776 * @param message Thread Message Pointer
777 */
linkEVMessage(SceNetAdhocMatchingContext * context,ThreadMessage * message)778 void linkEVMessage(SceNetAdhocMatchingContext * context, ThreadMessage * message)
779 {
780 	// Lock Access
781 	context->eventlock->lock();
782 
783 	// Link Message
784 	message->next = context->event_stack;
785 	context->event_stack = message;
786 
787 	// Unlock Access
788 	context->eventlock->unlock();
789 }
790 
791 /**
792 * Safely Link Thread Message to IO Thread Stack
793 * @param context Matching Context Pointer
794 * @param message Thread Message Pointer
795 */
linkIOMessage(SceNetAdhocMatchingContext * context,ThreadMessage * message)796 void linkIOMessage(SceNetAdhocMatchingContext * context, ThreadMessage * message)
797 {
798 	// Lock Access
799 	context->inputlock->lock();
800 
801 	// Link Message
802 	message->next = context->input_stack;
803 	context->input_stack = message;
804 
805 	// Unlock Access
806 	context->inputlock->unlock();
807 }
808 
809 /**
810 * Send Generic Thread Message
811 * @param context Matching Context Pointer
812 * @param stack ADHOC_MATCHING_EVENT_STACK or ADHOC_MATCHING_INPUT_STACK
813 * @param mac Target MAC
814 * @param opcode Message Opcode
815 * @param optlen Optional Data Length
816 * @param opt Optional Data
817 */
sendGenericMessage(SceNetAdhocMatchingContext * context,int stack,SceNetEtherAddr * mac,int opcode,int optlen,const void * opt)818 void sendGenericMessage(SceNetAdhocMatchingContext * context, int stack, SceNetEtherAddr * mac, int opcode, int optlen, const void * opt)
819 {
820 	// Calculate Required Memory Size
821 	uint32_t size = sizeof(ThreadMessage) + optlen;
822 
823 	// Allocate Memory
824 	uint8_t * memory = (uint8_t *)malloc(size);
825 
826 	// Allocated Memory
827 	if (memory != NULL)
828 	{
829 		// Clear Memory
830 		memset(memory, 0, size);
831 
832 		// Cast Header
833 		ThreadMessage * header = (ThreadMessage *)memory;
834 
835 		// Set Message Opcode
836 		header->opcode = opcode;
837 
838 		// Set Peer MAC Address
839 		header->mac = *mac;
840 
841 		// Set Optional Data Length
842 		header->optlen = optlen;
843 
844 		// Set Optional Data
845 		memcpy(memory + sizeof(ThreadMessage), opt, optlen);
846 
847 		// Link Thread Message
848 		if (stack == PSP_ADHOC_MATCHING_EVENT_STACK) linkEVMessage(context, header);
849 
850 		// Link Thread Message to Input Stack
851 		else linkIOMessage(context, header);
852 
853 		// Exit Function
854 		return;
855 	}
856 
857 	peerlock.lock();
858 	// Out of Memory Emergency Delete
859 	auto peer = findPeer(context, mac);
860 	deletePeer(context, peer);
861 	peerlock.unlock();
862 }
863 
864 /**
865 * Send Accept Message from P2P -> P2P or Parent -> Children
866 * @param context Matching Context Pointer
867 * @param peer Target Peer
868 * @param optlen Optional Data Length
869 * @param opt Optional Data
870 */
sendAcceptMessage(SceNetAdhocMatchingContext * context,SceNetAdhocMatchingMemberInternal * peer,int optlen,const void * opt)871 void sendAcceptMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer, int optlen, const void * opt)
872 {
873 	// Send Accept Message
874 	sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_ACCEPT, optlen, opt);
875 }
876 
877 /**
878 * Send Join Request from P2P -> P2P or Children -> Parent
879 * @param context Matching Context Pointer
880 * @param peer Target Peer
881 * @param optlen Optional Data Length
882 * @param opt Optional Data
883 */
sendJoinRequest(SceNetAdhocMatchingContext * context,SceNetAdhocMatchingMemberInternal * peer,int optlen,const void * opt)884 void sendJoinRequest(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer, int optlen, const void * opt)
885 {
886 	// Send Join Message
887 	sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_JOIN, optlen, opt);
888 }
889 
890 /**
891 * Send Cancel Message to Peer (has various effects)
892 * @param context Matching Context Pointer
893 * @param peer Target Peer
894 * @param optlen Optional Data Length
895 * @param opt Optional Data
896 */
sendCancelMessage(SceNetAdhocMatchingContext * context,SceNetAdhocMatchingMemberInternal * peer,int optlen,const void * opt)897 void sendCancelMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer, int optlen, const void * opt)
898 {
899 	// Send Cancel Message
900 	sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_CANCEL, optlen, opt);
901 }
902 
903 /**
904 * Send Bulk Data to Peer
905 * @param context Matching Context Pointer
906 * @param peer Target Peer
907 * @param datalen Data Length
908 * @param data Data
909 */
sendBulkData(SceNetAdhocMatchingContext * context,SceNetAdhocMatchingMemberInternal * peer,int datalen,const void * data)910 void sendBulkData(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer, int datalen, const void * data)
911 {
912 	// Send Bulk Data Message
913 	sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_BULK, datalen, data);
914 }
915 
916 /**
917 * Abort Bulk Data Transfer (if in progress)
918 * @param context Matching Context Pointer
919 * @param peer Target Peer
920 */
abortBulkTransfer(SceNetAdhocMatchingContext * context,SceNetAdhocMatchingMemberInternal * peer)921 void abortBulkTransfer(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer)
922 {
923 	// Send Bulk Data Abort Message
924 	sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_BULK_ABORT, 0, NULL);
925 }
926 
927 /**
928 * Notify all established Peers about new Kid in the Neighborhood
929 * @param context Matching Context Pointer
930 * @param peer New Kid
931 */
sendBirthMessage(SceNetAdhocMatchingContext * context,SceNetAdhocMatchingMemberInternal * peer)932 void sendBirthMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer)
933 {
934 	// Send Birth Message
935 	sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_BIRTH, 0, NULL);
936 }
937 
938 /**
939 * Notify all established Peers about abandoned Child
940 * @param context Matching Context Pointer
941 * @param peer Abandoned Child
942 */
sendDeathMessage(SceNetAdhocMatchingContext * context,SceNetAdhocMatchingMemberInternal * peer)943 void sendDeathMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer)
944 {
945 	// Send Death Message
946 	sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_DEATH, 0, NULL);
947 }
948 
949 /**
950 * Return Number of Connected Peers
951 * @param context Matching Context Pointer
952 * @return Number of Connected Peers
953 */
countConnectedPeers(SceNetAdhocMatchingContext * context,const bool excludeTimedout)954 uint32_t countConnectedPeers(SceNetAdhocMatchingContext * context, const bool excludeTimedout)
955 {
956 	// Peer Count
957 	uint32_t count = 0;
958 
959 	// Parent Mode
960 	if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT)
961 	{
962 		// Number of Children + 1 Parent (Self)
963 		count = countChildren(context, excludeTimedout) + 1;
964 	}
965 
966 	// Child Mode
967 	else if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD)
968 	{
969 		// Default to 1 Child (Self)
970 		count = 1;
971 
972 		// Connected to Parent
973 		if (findParent(context) != NULL)
974 		{
975 			// Add Number of Siblings + 1 Parent
976 			count += countChildren(context, excludeTimedout) + 1; // Since count is already started from 1, Do we need to +1 here? Ys vs. Sora no Kiseki seems to show wrong number of players without +1 here
977 		}
978 	}
979 
980 	// P2P Mode
981 	else
982 	{
983 		// Default to 1 P2P Client (Self)
984 		count = 1;
985 
986 		// Connected to another P2P Client
987 		if (findP2P(context, excludeTimedout) != NULL)
988 		{
989 			// Add P2P Brother
990 			count++;
991 		}
992 	}
993 
994 	// Return Peer Count
995 	return count;
996 }
997 
998 /**
999 * Spawn Local Event for Event Thread
1000 * @param context Matching Context Pointer
1001 * @param event Event ID
1002 * @param mac Event Source MAC
1003 * @param optlen Optional Data Length
1004 * @param opt Optional Data
1005 */
spawnLocalEvent(SceNetAdhocMatchingContext * context,int event,SceNetEtherAddr * mac,int optlen,void * opt)1006 void spawnLocalEvent(SceNetAdhocMatchingContext * context, int event, SceNetEtherAddr * mac, int optlen, void * opt)
1007 {
1008 	// Spawn Local Event
1009 	sendGenericMessage(context, PSP_ADHOC_MATCHING_EVENT_STACK, mac, event, optlen, opt);
1010 }
1011 
1012 /**
1013 * Handle Timeouts in Matching Context
1014 * @param context Matching Context Pointer
1015 */
handleTimeout(SceNetAdhocMatchingContext * context)1016 void handleTimeout(SceNetAdhocMatchingContext * context)
1017 {
1018 	peerlock.lock();
1019 	// Iterate Peer List
1020 	SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
1021 	while (peer != NULL && contexts != NULL && coreState != CORE_POWERDOWN)
1022 	{
1023 		// Get Next Pointer (to avoid crash on memory freeing)
1024 		SceNetAdhocMatchingMemberInternal * next = peer->next;
1025 
1026 		u64_le now = CoreTiming::GetGlobalTimeUsScaled(); //time_now_d()*1000000.0
1027 		// Timeout! Apparently the latest GetGlobalTimeUsScaled (ie. now) have a possibility to be smaller than previous GetGlobalTimeUsScaled (ie. lastping) thus resulting a negative number when subtracted :(
1028 		if (peer->state != 0 && static_cast<s64>(now - peer->lastping) > static_cast<s64>(context->timeout))
1029 		{
1030 			// Spawn Timeout Event
1031 			if ((context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && peer->state == PSP_ADHOC_MATCHING_PEER_PARENT) ||
1032 				(context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) ||
1033 				(context->mode == PSP_ADHOC_MATCHING_MODE_P2P && peer->state == PSP_ADHOC_MATCHING_PEER_P2P)) {
1034 				// FIXME: TIMEOUT event should only be triggered on Parent/P2P mode and for Parent/P2P peer?
1035 				spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_TIMEOUT, &peer->mac, 0, NULL);
1036 
1037 				INFO_LOG(SCENET, "TimedOut Member Peer %s (%lld - %lld = %lld > %lld us)", mac2str(&peer->mac).c_str(), now, peer->lastping, (now - peer->lastping), context->timeout);
1038 
1039 				if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT)
1040 					sendDeathMessage(context, peer);
1041 				else
1042 					sendCancelMessage(context, peer, 0, NULL);
1043 			}
1044 		}
1045 
1046 		// Move Pointer
1047 		peer = next;
1048 	}
1049 	peerlock.unlock();
1050 }
1051 
1052 /**
1053 * Recursive Stack Cleaner
1054 * @param node Current Thread Message Node
1055 */
clearStackRecursive(ThreadMessage * & node)1056 void clearStackRecursive(ThreadMessage *& node)
1057 {
1058 	// Not End of List
1059 	if (node != NULL) clearStackRecursive(node->next);
1060 
1061 	// Free Last Existing Node of List (NULL is handled in _free)
1062 	free(node);
1063 	node = NULL;
1064 }
1065 
1066 /**
1067 * Clear Thread Stack
1068 * @param context Matching Context Pointer
1069 * @param stack ADHOC_MATCHING_EVENT_STACK or ADHOC_MATCHING_INPUT_STACK
1070 */
clearStack(SceNetAdhocMatchingContext * context,int stack)1071 void clearStack(SceNetAdhocMatchingContext * context, int stack)
1072 {
1073 	if (context == NULL) return;
1074 
1075 	// Clear Event Stack
1076 	if (stack == PSP_ADHOC_MATCHING_EVENT_STACK)
1077 	{
1078 		context->eventlock->lock();
1079 		// Free Memory Recursively
1080 		clearStackRecursive(context->event_stack);
1081 
1082 		// Destroy Reference
1083 		context->event_stack = NULL;
1084 
1085 		context->eventlock->unlock();
1086 	}
1087 
1088 	// Clear IO Stack
1089 	else
1090 	{
1091 		context->inputlock->lock();
1092 		// Free Memory Recursively
1093 		clearStackRecursive(context->input_stack);
1094 
1095 		// Destroy Reference
1096 		context->input_stack = NULL;
1097 
1098 		context->inputlock->unlock();
1099 	}
1100 }
1101 
1102 /**
1103 * Clear Peer List
1104 * @param context Matching Context Pointer
1105 */
clearPeerList(SceNetAdhocMatchingContext * context)1106 void clearPeerList(SceNetAdhocMatchingContext * context)
1107 {
1108 	// Acquire Peer Lock
1109 	peerlock.lock();
1110 
1111 	// Iterate Peer List
1112 	SceNetAdhocMatchingMemberInternal * peer = context->peerlist;
1113 	while (peer != NULL)
1114 	{
1115 		// Grab Next Pointer
1116 		context->peerlist = peer->next; //SceNetAdhocMatchingMemberInternal * next = peer->next;
1117 
1118 		// Delete Peer
1119 		free(peer); //deletePeer(context, peer);
1120 		// 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
1121 		//peer->lastping = CoreTiming::GetGlobalTimeUsScaled();
1122 
1123 		// Move Pointer
1124 		peer = context->peerlist; //peer = next;
1125 	}
1126 
1127 	// Free Peer Lock
1128 	peerlock.unlock();
1129 }
1130 
DoState(PointerWrap & p)1131 void AfterMatchingMipsCall::DoState(PointerWrap & p) {
1132 	auto s = p.Section("AfterMatchingMipsCall", 1, 4);
1133 	if (!s)
1134 		return;
1135 	if (s >= 1) {
1136 		Do(p, EventID);
1137 	} else {
1138 		EventID = -1;
1139 	}
1140 	if (s >= 4) {
1141 		Do(p, contextID);
1142 		Do(p, bufAddr);
1143 	} else {
1144 		contextID = -1;
1145 		bufAddr = 0;
1146 	}
1147 }
1148 
1149 // It seems After Actions being called in reverse order of Mipscall order (ie. MipsCall order of ACCEPT(6)->ESTABLISH(7) getting AfterAction order of ESTABLISH(7)->ACCEPT(6)
run(MipsCall & call)1150 void AfterMatchingMipsCall::run(MipsCall &call) {
1151 	if (context == NULL) {
1152 		peerlock.lock();
1153 		context = findMatchingContext(contextID);
1154 		peerlock.unlock();
1155 	}
1156 	u32 v0 = currentMIPS->r[MIPS_REG_V0];
1157 	if (__IsInInterrupt()) ERROR_LOG(SCENET, "AfterMatchingMipsCall::run [ID=%i][Event=%d] is Returning Inside an Interrupt!", contextID, EventID);
1158 	//SetMatchingInCallback(context, false);
1159 	DEBUG_LOG(SCENET, "AfterMatchingMipsCall::run [ID=%i][Event=%d][%s] [cbId: %u][retV0: %08x]", contextID, EventID, mac2str((SceNetEtherAddr*)Memory::GetPointer(bufAddr)).c_str(), call.cbId, v0);
1160 	if (Memory::IsValidAddress(bufAddr)) userMemory.Free(bufAddr);
1161 	//call.setReturnValue(v0);
1162 }
1163 
SetData(int ContextID,int eventId,u32_le BufAddr)1164 void AfterMatchingMipsCall::SetData(int ContextID, int eventId, u32_le BufAddr) {
1165 	contextID = ContextID;
1166 	EventID = eventId;
1167 	bufAddr = BufAddr;
1168 	peerlock.lock();
1169 	context = findMatchingContext(ContextID);
1170 	peerlock.unlock();
1171 }
1172 
SetMatchingInCallback(SceNetAdhocMatchingContext * context,bool IsInCB)1173 bool SetMatchingInCallback(SceNetAdhocMatchingContext* context, bool IsInCB) {
1174 	if (context == NULL) return false;
1175 	peerlock.lock();
1176 	context->IsMatchingInCB = IsInCB;
1177 	peerlock.unlock();
1178 	return IsInCB;
1179 }
1180 
IsMatchingInCallback(SceNetAdhocMatchingContext * context)1181 bool IsMatchingInCallback(SceNetAdhocMatchingContext* context) {
1182 	bool inCB = false;
1183 	if (context == NULL) return inCB;
1184 	peerlock.lock();
1185 	inCB = (context->IsMatchingInCB);
1186 	peerlock.unlock();
1187 	return inCB;
1188 }
1189 
DoState(PointerWrap & p)1190 void AfterAdhocMipsCall::DoState(PointerWrap & p) {
1191 	auto s = p.Section("AfterAdhocMipsCall", 1, 4);
1192 	if (!s)
1193 		return;
1194 	if (s >= 3) {
1195 		Do(p, HandlerID);
1196 		Do(p, EventID);
1197 		Do(p, argsAddr);
1198 	} else {
1199 		HandlerID = -1;
1200 		EventID = -1;
1201 		argsAddr = 0;
1202 	}
1203 }
1204 
run(MipsCall & call)1205 void AfterAdhocMipsCall::run(MipsCall& call) {
1206 	u32 v0 = currentMIPS->r[MIPS_REG_V0];
1207 	if (__IsInInterrupt()) ERROR_LOG(SCENET, "AfterAdhocMipsCall::run [ID=%i][Event=%d] is Returning Inside an Interrupt!", HandlerID, EventID);
1208 	SetAdhocctlInCallback(false);
1209 	isAdhocctlBusy = false;
1210 	DEBUG_LOG(SCENET, "AfterAdhocMipsCall::run [ID=%i][Event=%d] [cbId: %u][retV0: %08x]", HandlerID, EventID, call.cbId, v0);
1211 	//call.setReturnValue(v0);
1212 }
1213 
SetData(int handlerID,int eventId,u32_le ArgsAddr)1214 void AfterAdhocMipsCall::SetData(int handlerID, int eventId, u32_le ArgsAddr) {
1215 	HandlerID = handlerID;
1216 	EventID = eventId;
1217 	argsAddr = ArgsAddr;
1218 }
1219 
SetAdhocctlInCallback(bool IsInCB)1220 int SetAdhocctlInCallback(bool IsInCB) {
1221 	std::lock_guard<std::recursive_mutex> adhocGuard(adhocEvtMtx);
1222 	IsAdhocctlInCB += (IsInCB?1:-1);
1223 	return IsAdhocctlInCB;
1224 }
1225 
IsAdhocctlInCallback()1226 int IsAdhocctlInCallback() {
1227 	std::lock_guard<std::recursive_mutex> adhocGuard(adhocEvtMtx);
1228 	int inCB = IsAdhocctlInCB;
1229 	return inCB;
1230 }
1231 
1232 // Make sure MIPS calls have been fully executed before the next notifyAdhocctlHandlers
notifyAdhocctlHandlers(u32 flag,u32 error)1233 void notifyAdhocctlHandlers(u32 flag, u32 error) {
1234 	__UpdateAdhocctlHandlers(flag, error);
1235 }
1236 
1237 // Matching callback is void function: typedef void(*SceNetAdhocMatchingHandler)(int id, int event, SceNetEtherAddr * peer, int optlen, void * opt);
1238 // Important! The MIPS call need to be fully executed before the next MIPS call invoked, as the game (ie. DBZ Tag Team) may need to prepare something for the next callback event to use
1239 // Note: Must not lock peerlock within this function to prevent race-condition with other thread whos owning peerlock and trying to lock context->eventlock owned by this thread
notifyMatchingHandler(SceNetAdhocMatchingContext * context,ThreadMessage * msg,void * opt,u32_le & bufAddr,u32_le & bufLen,u32_le * args)1240 void notifyMatchingHandler(SceNetAdhocMatchingContext * context, ThreadMessage * msg, void * opt, u32_le &bufAddr, u32_le &bufLen, u32_le * args) {
1241 	// Don't share buffer address space with other mipscall in the queue since mipscalls aren't immediately executed
1242 	MatchingArgs argsNew = { 0 };
1243 	u32_le dataBufLen = msg->optlen + 8; //max(bufLen, msg->optlen + 8);
1244 	u32_le dataBufAddr = userMemory.Alloc(dataBufLen); // We will free this memory after returning from mipscall
1245 	uint8_t * dataPtr = Memory::GetPointer(dataBufAddr);
1246 	if (dataPtr) {
1247 		memcpy(dataPtr, &msg->mac, sizeof(msg->mac));
1248 		if (msg->optlen > 0)
1249 			memcpy(dataPtr + 8, opt, msg->optlen);
1250 
1251 		argsNew.data[1] = msg->opcode;
1252 		argsNew.data[2] = dataBufAddr;
1253 		argsNew.data[3] = msg->optlen;
1254 		argsNew.data[4] = dataBufAddr + 8; // OptData Addr
1255 	}
1256 	else {
1257 		argsNew.data[1] = PSP_ADHOC_MATCHING_EVENT_ERROR; // not sure where to put the error code for EVENT_ERROR tho
1258 	}
1259 	argsNew.data[0] = context->id;
1260 	argsNew.data[5] = context->handler.entryPoint; //not part of callback argument, just borrowing a space to store callback address so i don't need to search the context first later
1261 
1262 	// ScheduleEvent_Threadsafe_Immediate seems to get mixed up with interrupt (returning from mipscall inside an interrupt) and getting invalid address before returning from interrupt
1263 	__UpdateMatchingHandler(argsNew);
1264 }
1265 
freeFriendsRecursive(SceNetAdhocctlPeerInfo * node,int32_t * count)1266 void freeFriendsRecursive(SceNetAdhocctlPeerInfo * node, int32_t* count) {
1267 	// End of List
1268 	if (node == NULL) return;
1269 
1270 	// Increase Recursion Depth
1271 	freeFriendsRecursive(node->next, count);
1272 
1273 	// Free Memory
1274 	free(node);
1275 	node = NULL;
1276 	if (count != NULL) (*count)++;
1277 }
1278 
timeoutFriendsRecursive(SceNetAdhocctlPeerInfo * node,int32_t * count)1279 void timeoutFriendsRecursive(SceNetAdhocctlPeerInfo * node, int32_t* count) {
1280 	// End of List
1281 	if (node == NULL) return;
1282 
1283 	// Increase Recursion Depth
1284 	timeoutFriendsRecursive(node->next, count);
1285 
1286 	// Set last timestamp
1287 	node->last_recv = 0;
1288 	if (count != NULL) (*count)++;
1289 }
1290 
sendChat(std::string chatString)1291 void sendChat(std::string chatString) {
1292 	SceNetAdhocctlChatPacketC2S chat;
1293 	auto n = GetI18NCategory("Networking");
1294 	chat.base.opcode = OPCODE_CHAT;
1295 	//TODO check network inited, check send success or not, chatlog.pushback error on failed send, pushback error on not connected
1296 	if (friendFinderRunning) {
1297 		// Send Chat to Server
1298 		if (!chatString.empty()) {
1299 			//maximum char allowed is 64 character for compability with original server (pro.coldbird.net)
1300 			std::string message = chatString.substr(0, 60); // 64 return chat variable corrupted is it out of memory?
1301 			strcpy(chat.message, message.c_str());
1302 			//Send Chat Messages
1303 			if (IsSocketReady((int)metasocket, false, true) > 0) {
1304 				int chatResult = send((int)metasocket, (const char*)&chat, sizeof(chat), MSG_NOSIGNAL);
1305 				NOTICE_LOG(SCENET, "Send Chat %s to Adhoc Server", chat.message);
1306 				std::string name = g_Config.sNickName.c_str();
1307 
1308 				std::lock_guard<std::mutex> guard(chatLogLock);
1309 				chatLog.push_back(name.substr(0, 8) + ": " + chat.message);
1310 				chatMessageGeneration++;
1311 			}
1312 		}
1313 	} else {
1314 		std::lock_guard<std::mutex> guard(chatLogLock);
1315 		chatLog.push_back(n->T("You're in Offline Mode, go to lobby or online hall"));
1316 		chatMessageGeneration++;
1317 	}
1318 }
1319 
getChatLog()1320 std::vector<std::string> getChatLog() {
1321 	std::lock_guard<std::mutex> guard(chatLogLock);
1322 	// If the log gets large, trim it down.
1323 	if (chatLog.size() > 50) {
1324 		chatLog.erase(chatLog.begin(), chatLog.begin() + (chatLog.size() - 50));
1325 	}
1326 	return chatLog;
1327 }
1328 
GetChatChangeID()1329 int GetChatChangeID() {
1330 	return chatMessageGeneration;
1331 }
1332 
GetChatMessageCount()1333 int GetChatMessageCount() {
1334 	return chatMessageCount;
1335 }
1336 
1337 // TODO: We should probably change this thread into PSPThread (or merging it into the existing AdhocThread PSPThread) as there are too many global vars being used here which also being used within some HLEs
friendFinder()1338 int friendFinder(){
1339 	SetCurrentThreadName("FriendFinder");
1340 	auto n = GetI18NCategory("Networking");
1341 	// Receive Buffer
1342 	int rxpos = 0;
1343 	uint8_t rx[1024];
1344 
1345 	// Chat Packet
1346 	SceNetAdhocctlChatPacketC2S chat;
1347 	chat.base.opcode = OPCODE_CHAT;
1348 
1349 	// Last Ping Time
1350 	uint64_t lastping = 0;
1351 
1352 	// Last Time Reception got updated
1353 	uint64_t lastreceptionupdate = 0;
1354 
1355 	uint64_t now;
1356 
1357 	// Log Startup
1358 	INFO_LOG(SCENET, "FriendFinder: Begin of Friend Finder Thread");
1359 
1360 	// Resolve and cache AdhocServer DNS
1361 	addrinfo* resolved = nullptr;
1362 	std::string err;
1363 	g_adhocServerIP.in.sin_addr.s_addr = INADDR_NONE;
1364 	if (g_Config.bEnableWlan && !net::DNSResolve(g_Config.proAdhocServer, "", &resolved, err)) {
1365 		ERROR_LOG(SCENET, "DNS Error Resolving %s\n", g_Config.proAdhocServer.c_str());
1366 		host->NotifyUserMessage(n->T("DNS Error Resolving ") + g_Config.proAdhocServer, 2.0f, 0x0000ff);
1367 	}
1368 	if (resolved) {
1369 		for (auto ptr = resolved; ptr != NULL; ptr = ptr->ai_next) {
1370 			switch (ptr->ai_family) {
1371 			case AF_INET:
1372 				g_adhocServerIP.in = *(sockaddr_in*)ptr->ai_addr;
1373 				break;
1374 			}
1375 		}
1376 		net::DNSResolveFree(resolved);
1377 	}
1378 	g_adhocServerIP.in.sin_port = htons(SERVER_PORT);
1379 
1380 	// Finder Loop
1381 	friendFinderRunning = true;
1382 	while (friendFinderRunning) {
1383 		// Acquire Network Lock
1384 		//_acquireNetworkLock();
1385 
1386 		// Reconnect when disconnected while Adhocctl is still inited
1387 		if (metasocket == (int)INVALID_SOCKET && netAdhocctlInited && isAdhocctlNeedLogin) {
1388 			if (g_Config.bEnableWlan) {
1389 				if (initNetwork(&product_code) == 0) {
1390 					networkInited = true;
1391 					INFO_LOG(SCENET, "FriendFinder: Network [RE]Initialized");
1392 					// At this point we are most-likely not in a Group within the Adhoc Server, so we should probably reset AdhocctlState
1393 					adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
1394 					netAdhocGameModeEntered = false;
1395 					isAdhocctlBusy = false;
1396 				}
1397 				else {
1398 					networkInited = false;
1399 					shutdown((int)metasocket, SD_BOTH);
1400 					closesocket((int)metasocket);
1401 					metasocket = (int)INVALID_SOCKET;
1402 				}
1403 			}
1404 		}
1405 		// Prevent retrying to Login again unless it was on demand
1406 		isAdhocctlNeedLogin = false;
1407 
1408 		if (networkInited) {
1409 			// Ping Server
1410 			now = time_now_d() * 1000000.0; // Use time_now_d()*1000000.0 instead of CoreTiming::GetGlobalTimeUsScaled() if the game gets disconnected from AdhocServer too soon when FPS wasn't stable
1411 			// original code : ((sceKernelGetSystemTimeWide() - lastping) >= ADHOCCTL_PING_TIMEOUT)
1412 			if (static_cast<s64>(now - lastping) >= PSP_ADHOCCTL_PING_TIMEOUT) { // We may need to use lower interval to prevent getting timeout at Pro Adhoc Server through internet
1413 				// Prepare Packet
1414 				uint8_t opcode = OPCODE_PING;
1415 
1416 				// Send Ping to Server, may failed with socket error 10054/10053 if someone else with the same IP already connected to AdHoc Server (the server might need to be modified to differentiate MAC instead of IP)
1417 				if (IsSocketReady((int)metasocket, false, true) > 0) {
1418 					int iResult = send((int)metasocket, (const char*)&opcode, 1, MSG_NOSIGNAL);
1419 					int error = errno;
1420 					// KHBBS seems to be getting error 10053 often
1421 					if (iResult == SOCKET_ERROR) {
1422 						ERROR_LOG(SCENET, "FriendFinder: Socket Error (%i) when sending OPCODE_PING", error);
1423 						if (error != EAGAIN && error != EWOULDBLOCK) {
1424 							networkInited = false;
1425 							shutdown((int)metasocket, SD_BOTH);
1426 							closesocket((int)metasocket);
1427 							metasocket = (int)INVALID_SOCKET;
1428 							host->NotifyUserMessage(std::string(n->T("Disconnected from AdhocServer")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(error) + ")", 2.0, 0x0000ff);
1429 							// Mark all friends as timedout since we won't be able to detects disconnected friends anymore without being connected to Adhoc Server
1430 							peerlock.lock();
1431 							timeoutFriendsRecursive(friends);
1432 							peerlock.unlock();
1433 						}
1434 					}
1435 					else {
1436 						// Update Ping Time
1437 						lastping = now;
1438 						VERBOSE_LOG(SCENET, "FriendFinder: Sending OPCODE_PING (%llu)", static_cast<unsigned long long>(now));
1439 					}
1440 				}
1441 			}
1442 
1443 			// Check for Incoming Data
1444 			if (IsSocketReady((int)metasocket, true, false) > 0) {
1445 				int received = recv((int)metasocket, (char*)(rx + rxpos), sizeof(rx) - rxpos, MSG_NOSIGNAL);
1446 
1447 				// Free Network Lock
1448 				//_freeNetworkLock();
1449 
1450 				// Received Data
1451 				if (received > 0) {
1452 					// Fix Position
1453 					rxpos += received;
1454 
1455 					// Log Incoming Traffic
1456 					//printf("Received %d Bytes of Data from Server\n", received);
1457 					INFO_LOG(SCENET, "Received %d Bytes of Data from Adhoc Server", received);
1458 				}
1459 			}
1460 
1461 			// Calculate EnterGameMode Timeout to prevent waiting forever for disconnected players
1462 			if (isAdhocctlBusy && adhocctlState == ADHOCCTL_STATE_DISCONNECTED && adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE && netAdhocGameModeEntered && static_cast<s64>(now - adhocctlStartTime) > netAdhocEnterGameModeTimeout) {
1463 				netAdhocGameModeEntered = false;
1464 				notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, ERROR_NET_ADHOC_TIMEOUT);
1465 			}
1466 
1467 			// Handle Packets
1468 			if (rxpos > 0) {
1469 				// BSSID Packet
1470 				if (rx[0] == OPCODE_CONNECT_BSSID) {
1471 					// Enough Data available
1472 					if (rxpos >= (int)sizeof(SceNetAdhocctlConnectBSSIDPacketS2C)) {
1473 						// Cast Packet
1474 						SceNetAdhocctlConnectBSSIDPacketS2C* packet = (SceNetAdhocctlConnectBSSIDPacketS2C*)rx;
1475 
1476 						INFO_LOG(SCENET, "FriendFinder: Incoming OPCODE_CONNECT_BSSID [%s]", mac2str(&packet->mac).c_str());
1477 						// Update User BSSID
1478 						parameter.bssid.mac_addr = packet->mac; // This packet seems to contains Adhoc Group Creator's BSSID (similar to AP's BSSID) so it shouldn't get mixed up with local MAC address
1479 
1480 						// From JPCSP: Some games have problems when the PSP_ADHOCCTL_EVENT_CONNECTED is sent too quickly after connecting to a network. The connection will be set CONNECTED with a small delay (200ms or 200us?)
1481 						// Notify Event Handlers
1482 						if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {
1483 							SceNetEtherAddr localMac;
1484 							getLocalMac(&localMac);
1485 							if (std::find_if(gameModeMacs.begin(), gameModeMacs.end(),
1486 								[localMac](SceNetEtherAddr const& e) {
1487 									return isMacMatch(&e, &localMac);
1488 								}) == gameModeMacs.end()) {
1489 								// Arrange the order to be consistent on all players (Host on top), Starting from our self the rest of new players will be added to the back
1490 								gameModeMacs.push_back(localMac);
1491 
1492 								// FIXME: OPCODE_CONNECT_BSSID only triggered once, but the timing of ADHOCCTL_EVENT_GAME notification could be too soon, since there could be more players that need to join before the event should be notified
1493 								if (netAdhocGameModeEntered && gameModeMacs.size() >= requiredGameModeMacs.size()) {
1494 									notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);
1495 								}
1496 							}
1497 							else
1498 								WARN_LOG(SCENET, "GameMode SelfMember [%s] Already Existed!", mac2str(&localMac).c_str());
1499 						}
1500 						else {
1501 							//adhocctlState = ADHOCCTL_STATE_CONNECTED;
1502 							notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0);
1503 						}
1504 
1505 						// Give time a little time
1506 						//sceKernelDelayThread(adhocEventDelayMS * 1000);
1507 						//sleep_ms(adhocEventDelayMS);
1508 
1509 						// Move RX Buffer
1510 						memmove(rx, rx + sizeof(SceNetAdhocctlConnectBSSIDPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlConnectBSSIDPacketS2C));
1511 
1512 						// Fix RX Buffer Length
1513 						rxpos -= sizeof(SceNetAdhocctlConnectBSSIDPacketS2C);
1514 					}
1515 				}
1516 
1517 				// Chat Packet
1518 				else if (rx[0] == OPCODE_CHAT) {
1519 					// Enough Data available
1520 					if (rxpos >= (int)sizeof(SceNetAdhocctlChatPacketS2C)) {
1521 						// Cast Packet
1522 						SceNetAdhocctlChatPacketS2C* packet = (SceNetAdhocctlChatPacketS2C*)rx;
1523 						INFO_LOG(SCENET, "FriendFinder: Incoming OPCODE_CHAT");
1524 
1525 						// Fix strings with null-terminated
1526 						packet->name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;
1527 						packet->base.message[ADHOCCTL_MESSAGE_LEN - 1] = 0;
1528 
1529 						// Add Incoming Chat to HUD
1530 						NOTICE_LOG(SCENET, "Received chat message %s", packet->base.message);
1531 						std::string incoming = "";
1532 						std::string name = (char*)packet->name.data;
1533 						incoming.append(name.substr(0, 8));
1534 						incoming.append(": ");
1535 						incoming.append((char*)packet->base.message);
1536 
1537 						std::lock_guard<std::mutex> guard(chatLogLock);
1538 						chatLog.push_back(incoming);
1539 						chatMessageGeneration++;
1540 						chatMessageCount++;
1541 
1542 						// Move RX Buffer
1543 						memmove(rx, rx + sizeof(SceNetAdhocctlChatPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlChatPacketS2C));
1544 
1545 						// Fix RX Buffer Length
1546 						rxpos -= sizeof(SceNetAdhocctlChatPacketS2C);
1547 					}
1548 				}
1549 
1550 				// Connect Packet
1551 				else if (rx[0] == OPCODE_CONNECT) {
1552 					// Enough Data available
1553 					if (rxpos >= (int)sizeof(SceNetAdhocctlConnectPacketS2C)) {
1554 						// Cast Packet
1555 						SceNetAdhocctlConnectPacketS2C* packet = (SceNetAdhocctlConnectPacketS2C*)rx;
1556 
1557 						// Fix strings with null-terminated
1558 						packet->name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;
1559 
1560 						// Log Incoming Peer
1561                         u32_le ipaddr = packet->ip;
1562 						INFO_LOG(SCENET, "FriendFinder: Incoming OPCODE_CONNECT [%s][%s][%s]", mac2str(&packet->mac).c_str(), ip2str(*(in_addr*)&ipaddr).c_str(), packet->name.data);
1563 
1564 						// Add User
1565 						addFriend(packet);
1566 
1567 						// Make sure GameMode participants are all joined (including self MAC)
1568 						if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {
1569 							if (std::find_if(gameModeMacs.begin(), gameModeMacs.end(),
1570 								[packet](SceNetEtherAddr const& e) {
1571 									return isMacMatch(&e, &packet->mac);
1572 								}) == gameModeMacs.end()) {
1573 								// Arrange the order to be consistent on all players (Host on top), Existing players are sent in reverse by AdhocServer
1574 								SceNetEtherAddr localMac;
1575 								getLocalMac(&localMac);
1576 								auto it = std::find_if(gameModeMacs.begin(), gameModeMacs.end(),
1577 									[localMac](SceNetEtherAddr const& e) {
1578 										return isMacMatch(&e, &localMac);
1579 									});
1580 								// Starting from our self the rest of new players will be added to the back
1581 								if (it != gameModeMacs.end()) {
1582 									gameModeMacs.push_back(packet->mac);
1583 								}
1584 								else {
1585 									it = gameModeMacs.begin() + 1;
1586 									gameModeMacs.insert(it, packet->mac);
1587 								}
1588 
1589 								// From JPCSP: Join complete when all the required MACs have joined
1590 								if (netAdhocGameModeEntered && requiredGameModeMacs.size() > 0 && gameModeMacs.size() == requiredGameModeMacs.size()) {
1591 									// TODO: Should we replace gameModeMacs contents with requiredGameModeMacs contents to make sure they are in the same order with macs from sceNetAdhocctlCreateEnterGameMode? But may not be consistent with the list on client side!
1592 									//gameModeMacs = requiredGameModeMacs;
1593 									notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);
1594 								}
1595 							}
1596 							else
1597 								WARN_LOG(SCENET, "GameMode Member [%s] Already Existed!", mac2str(&packet->mac).c_str());
1598 						}
1599 
1600 						// Update HUD User Count
1601 						std::string name = (char*)packet->name.data;
1602 						std::string incoming = "";
1603 						incoming.append(name.substr(0, 8));
1604 						incoming.append(" Joined ");
1605 						//do we need ip?
1606 						//joined.append((char *)packet->ip);
1607 
1608 						std::lock_guard<std::mutex> guard(chatLogLock);
1609 						chatLog.push_back(incoming);
1610 						chatMessageGeneration++;
1611 
1612 #ifdef LOCALHOST_AS_PEER
1613 						setUserCount(getActivePeerCount());
1614 #else
1615 						// setUserCount(getActivePeerCount()+1);
1616 #endif
1617 
1618 						// Move RX Buffer
1619 						memmove(rx, rx + sizeof(SceNetAdhocctlConnectPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlConnectPacketS2C));
1620 
1621 						// Fix RX Buffer Length
1622 						rxpos -= sizeof(SceNetAdhocctlConnectPacketS2C);
1623 					}
1624 				}
1625 
1626 				// Disconnect Packet
1627 				else if (rx[0] == OPCODE_DISCONNECT) {
1628 					// Enough Data available
1629 					if (rxpos >= (int)sizeof(SceNetAdhocctlDisconnectPacketS2C)) {
1630 						// Cast Packet
1631 						SceNetAdhocctlDisconnectPacketS2C* packet = (SceNetAdhocctlDisconnectPacketS2C*)rx;
1632 
1633 						DEBUG_LOG(SCENET, "FriendFinder: OPCODE_DISCONNECT");
1634 
1635 						// Log Incoming Peer Delete Request
1636 						INFO_LOG(SCENET, "FriendFinder: Incoming Peer Data Delete Request...");
1637 
1638 						if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {
1639 							auto peer = findFriendByIP(packet->ip);
1640 							for (auto& gma : replicaGameModeAreas)
1641 								if (isMacMatch(&gma.mac, &peer->mac_addr)) {
1642 									gma.updateTimestamp = 0;
1643 									break;
1644 								}
1645 						}
1646 
1647 						// Delete User by IP, should delete by MAC since IP can be shared (behind NAT) isn't?
1648 						deleteFriendByIP(packet->ip);
1649 
1650 						// Update HUD User Count
1651 #ifdef LOCALHOST_AS_PEER
1652 						setUserCount(_getActivePeerCount());
1653 #else
1654 					//setUserCount(_getActivePeerCount()+1);
1655 #endif
1656 
1657 					// Move RX Buffer
1658 						memmove(rx, rx + sizeof(SceNetAdhocctlDisconnectPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlDisconnectPacketS2C));
1659 
1660 						// Fix RX Buffer Length
1661 						rxpos -= sizeof(SceNetAdhocctlDisconnectPacketS2C);
1662 					}
1663 				}
1664 
1665 				// Scan Packet
1666 				else if (rx[0] == OPCODE_SCAN) {
1667 					// Enough Data available
1668 					if (rxpos >= (int)sizeof(SceNetAdhocctlScanPacketS2C)) {
1669 						// Cast Packet
1670 						SceNetAdhocctlScanPacketS2C* packet = (SceNetAdhocctlScanPacketS2C*)rx;
1671 
1672 						DEBUG_LOG(SCENET, "FriendFinder: OPCODE_SCAN");
1673 
1674 						// Log Incoming Network Information
1675 						INFO_LOG(SCENET, "Incoming Group Information...");
1676 
1677 						// Multithreading Lock
1678 						peerlock.lock();
1679 
1680 						// Allocate Structure Data
1681 						SceNetAdhocctlScanInfo* group = (SceNetAdhocctlScanInfo*)malloc(sizeof(SceNetAdhocctlScanInfo));
1682 
1683 						// Allocated Structure Data
1684 						if (group != NULL) {
1685 							// Clear Memory, should this be done only when allocating new group?
1686 							memset(group, 0, sizeof(SceNetAdhocctlScanInfo));
1687 
1688 							// Link to existing Groups
1689 							group->next = newnetworks;
1690 
1691 							// Copy Group Name
1692 							group->group_name = packet->group;
1693 
1694 							// Set Group Host
1695 							group->bssid.mac_addr = packet->mac;
1696 
1697 							// Set group parameters
1698 							// Since 0 is not a valid active channel we fake the channel for 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?)
1699 							group->channel = parameter.channel; //(parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) ? defaultWlanChannel : parameter.channel;
1700 							// This Mode should be a valid mode (>=0), probably should be sent by AdhocServer since there are 2 possibilities (Normal and GameMode). Air Conflicts - Aces Of World War 2 (which use GameMode) seems to relies on this Mode value.
1701 							group->mode = std::max(ADHOCCTL_MODE_NORMAL, adhocctlCurrentMode); // default to ADHOCCTL_MODE_NORMAL
1702 
1703 							// Link into Group List
1704 							newnetworks = group;
1705 						}
1706 
1707 						// Multithreading Unlock
1708 						peerlock.unlock();
1709 
1710 						// Move RX Buffer
1711 						memmove(rx, rx + sizeof(SceNetAdhocctlScanPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlScanPacketS2C));
1712 
1713 						// Fix RX Buffer Length
1714 						rxpos -= sizeof(SceNetAdhocctlScanPacketS2C);
1715 					}
1716 				}
1717 
1718 				// Scan Complete Packet
1719 				else if (rx[0] == OPCODE_SCAN_COMPLETE) {
1720 					DEBUG_LOG(SCENET, "FriendFinder: OPCODE_SCAN_COMPLETE");
1721 					// Log Scan Completion
1722 					INFO_LOG(SCENET, "FriendFinder: Incoming Scan complete response...");
1723 
1724 					// Reset current networks to prevent disbanded host to be listed again
1725 					peerlock.lock();
1726 					if (networks != newnetworks) {
1727 						freeGroupsRecursive(networks);
1728 						networks = newnetworks;
1729 					}
1730 					newnetworks = NULL;
1731 					peerlock.unlock();
1732 
1733 					// Notify Event Handlers
1734 					notifyAdhocctlHandlers(ADHOCCTL_EVENT_SCAN, 0);
1735 
1736 					// Move RX Buffer
1737 					memmove(rx, rx + 1, sizeof(rx) - 1);
1738 
1739 					// Fix RX Buffer Length
1740 					rxpos -= 1;
1741 				}
1742 			}
1743 		}
1744 		// This delay time should be 100ms when there is an event otherwise 500ms ?
1745 		sleep_ms(10); // Using 1ms for faster response just like AdhocServer?
1746 
1747 		// Don't do anything if it's paused, otherwise the log will be flooded
1748 		while (Core_IsStepping() && coreState != CORE_POWERDOWN && friendFinderRunning) sleep_ms(10);
1749 	}
1750 
1751 	// Groups/Networks should be deallocated isn't?
1752 
1753 	// Prevent the games from having trouble to reInitiate Adhoc (the next NetInit -> PdpCreate after NetTerm)
1754 	adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
1755 	friendFinderRunning = false;
1756 
1757 	// Log Shutdown
1758 	INFO_LOG(SCENET, "FriendFinder: End of Friend Finder Thread");
1759 
1760 	// Return Success
1761 	return 0;
1762 }
1763 
getActivePeerCount(const bool excludeTimedout)1764 int getActivePeerCount(const bool excludeTimedout) {
1765 	// Counter
1766 	int count = 0;
1767 
1768 	// #ifdef LOCALHOST_AS_PEER
1769 	// // Increase for Localhost
1770 	// count++;
1771 	// #endif
1772 
1773 	// Peer Reference
1774 	SceNetAdhocctlPeerInfo * peer = friends;
1775 
1776 	// Iterate Peers
1777 	for (; peer != NULL; peer = peer->next) {
1778 		// Increase Counter, Should we exclude peers pending for timed out?
1779 		if (!excludeTimedout || peer->last_recv != 0)
1780 			count++;
1781 	}
1782 
1783 	// Return Result
1784 	return count;
1785 }
1786 
getLocalIp(sockaddr_in * SocketAddress)1787 int getLocalIp(sockaddr_in* SocketAddress) {
1788 	if (isLocalServer) {
1789 		SocketAddress->sin_addr = g_localhostIP.in.sin_addr;
1790 		return 0;
1791 	}
1792 
1793 #if !PPSSPP_PLATFORM(SWITCH)
1794 	if (metasocket != (int)INVALID_SOCKET) {
1795 		struct sockaddr_in localAddr;
1796 		localAddr.sin_addr.s_addr = INADDR_ANY;
1797 		socklen_t addrLen = sizeof(localAddr);
1798 		int ret = getsockname((int)metasocket, (struct sockaddr*)&localAddr, &addrLen);
1799 		if (SOCKET_ERROR != ret) {
1800 			SocketAddress->sin_addr = localAddr.sin_addr;
1801 			return 0;
1802 		}
1803 	}
1804 #endif // !PPSSPP_PLATFORM(SWITCH)
1805 
1806 // Fallback if not connected to AdhocServer
1807 #if defined(_WIN32)
1808 	// Get local host name
1809 	char szHostName[256] = "";
1810 
1811 	if (::gethostname(szHostName, sizeof(szHostName))) {
1812 		// Error handling
1813 	}
1814 	// Get local network IP addresses (LAN/VPN/loopback)
1815 	struct addrinfo hints, * res = 0;
1816 	memset(&hints, 0, sizeof(hints));
1817 	hints.ai_family = AF_INET; // AF_UNSPEC;
1818 	hints.ai_socktype = SOCK_DGRAM;
1819 	hints.ai_flags = AI_ADDRCONFIG; // getaddrinfo with AI_ADDRCONFIG will fail when there is no local network connected? https://github.com/stephane/libmodbus/issues/575
1820 	// Note: getaddrinfo could cause freezes on Android if there is no network https://github.com/hrydgard/ppsspp/issues/13300
1821 	if (getaddrinfo(szHostName, NULL, &hints, &res) == 0 && res != NULL) {
1822 		memcpy(&SocketAddress->sin_addr, &((struct sockaddr_in*)res->ai_addr)->sin_addr, sizeof(SocketAddress->sin_addr));
1823 		freeaddrinfo(res);
1824 		return 0;
1825 	}
1826 
1827 #elif defined(getifaddrs) // On Android: Requires __ANDROID_API__ >= 24
1828 	struct ifaddrs* ifAddrStruct = NULL;
1829 	struct ifaddrs* ifa = NULL;
1830 
1831 	getifaddrs(&ifAddrStruct);
1832 	if (ifAddrStruct != NULL) {
1833 		for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
1834 			if (!ifa->ifa_addr) {
1835 				continue;
1836 			}
1837 			if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4
1838 				// is a valid IP4 Address
1839 				SocketAddress->sin_addr = ((struct sockaddr_in*)ifa->ifa_addr)->sin_addr;
1840 				break;
1841 			}
1842 		}
1843 		freeifaddrs(ifAddrStruct);
1844 		return 0;
1845 	}
1846 
1847 #else // Alternative way
1848 	int sock = socket(AF_INET, SOCK_DGRAM, 0);
1849 	if (sock != SOCKET_ERROR) {
1850 		const char* kGoogleDnsIp = "8.8.8.8"; // Needs to be an IP string so it can be resolved as fast as possible to IP, doesn't need to be reachable
1851 		uint16_t kDnsPort = 53;
1852 		struct sockaddr_in serv;
1853 		memset(&serv, 0, sizeof(serv));
1854 		serv.sin_family = AF_INET;
1855 		serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp);
1856 		serv.sin_port = htons(kDnsPort);
1857 
1858 		int err = connect(sock, (struct sockaddr*)&serv, sizeof(serv));
1859 		if (err != SOCKET_ERROR) {
1860 			struct sockaddr_in name;
1861 			socklen_t namelen = sizeof(name);
1862 			err = getsockname(sock, (struct sockaddr*)&name, &namelen);
1863 			if (err != SOCKET_ERROR) {
1864 				SocketAddress->sin_addr = name.sin_addr; // May be we should cache this so it doesn't need to use connect all the time, or even better cache it when connecting to adhoc server to get an accurate IP
1865 				closesocket(sock);
1866 				return 0;
1867 			}
1868 		}
1869 		closesocket(sock);
1870 	}
1871 #endif
1872 	return -1;
1873 }
1874 
getLocalIp(int sock)1875 uint32_t getLocalIp(int sock) {
1876 	struct sockaddr_in localAddr;
1877 	localAddr.sin_addr.s_addr = INADDR_ANY;
1878 	socklen_t addrLen = sizeof(localAddr);
1879 	getsockname(sock, (struct sockaddr*)&localAddr, &addrLen);
1880 	if (isLocalServer) {
1881 		localAddr.sin_addr = g_localhostIP.in.sin_addr;
1882 	}
1883 	return localAddr.sin_addr.s_addr;
1884 }
1885 
InitPrivateIPRanges()1886 static std::vector<std::pair<uint32_t, uint32_t>> InitPrivateIPRanges() {
1887 	struct sockaddr_in saNet, saMask;
1888 	std::vector<std::pair<uint32_t, uint32_t>> ip_ranges;
1889 
1890 	if (1 == inet_pton(AF_INET, "192.168.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.255.0.0", &(saMask.sin_addr)))
1891 		ip_ranges.push_back({saNet.sin_addr.s_addr, saMask.sin_addr.s_addr});
1892 	if (1 == inet_pton(AF_INET, "172.16.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.240.0.0", &(saMask.sin_addr)))
1893 		ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });
1894 	if (1 == inet_pton(AF_INET, "10.0.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.0.0.0", &(saMask.sin_addr)))
1895 		ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });
1896 	if (1 == inet_pton(AF_INET, "127.0.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.0.0.0", &(saMask.sin_addr)))
1897 		ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });
1898 	if (1 == inet_pton(AF_INET, "169.254.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.255.0.0", &(saMask.sin_addr)))
1899 		ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });
1900 
1901 	return ip_ranges;
1902 }
1903 
isPrivateIP(uint32_t ip)1904 bool isPrivateIP(uint32_t ip) {
1905 	static const std::vector<std::pair<uint32_t, uint32_t>> ip_ranges = InitPrivateIPRanges();
1906 	for (auto ipRange : ip_ranges) {
1907 		if ((ip & ipRange.second) == (ipRange.first & ipRange.second)) // We can just use ipRange.first directly if it's already correctly formatted
1908 			return true;
1909 	}
1910 	return false;
1911 }
1912 
isLoopbackIP(uint32_t ip)1913 bool isLoopbackIP(uint32_t ip) {
1914 	return ((uint8_t*)&ip)[0] == 0x7f;
1915 }
1916 
getLocalMac(SceNetEtherAddr * addr)1917 void getLocalMac(SceNetEtherAddr * addr){
1918 	// Read MAC Address from config
1919 	uint8_t mac[ETHER_ADDR_LEN] = {0};
1920 	if (PPSSPP_ID > 1) {
1921 		memset(&mac, PPSSPP_ID, sizeof(mac));
1922 		// Making sure the 1st 2-bits on the 1st byte of OUI are zero to prevent issue with some games (ie. Gran Turismo)
1923 		mac[0] &= 0xfc;
1924 	}
1925 	else
1926 	if (!ParseMacAddress(g_Config.sMACAddress.c_str(), mac)) {
1927 		ERROR_LOG(SCENET, "Error parsing mac address %s", g_Config.sMACAddress.c_str());
1928 		memset(&mac, 0, sizeof(mac));
1929 	}
1930 	memcpy(addr, mac, ETHER_ADDR_LEN);
1931 }
1932 
getLocalPort(int sock)1933 uint16_t getLocalPort(int sock) {
1934 	struct sockaddr_in localAddr;
1935 	localAddr.sin_port = 0;
1936 	socklen_t addrLen = sizeof(localAddr);
1937 	getsockname(sock, (struct sockaddr*)&localAddr, &addrLen);
1938 	return ntohs(localAddr.sin_port);
1939 }
1940 
getAvailToRecv(int sock,int udpBufferSize)1941 u_long getAvailToRecv(int sock, int udpBufferSize) {
1942 	u_long n = 0; // Typical MTU size is 1500
1943 	int err = -1;
1944 #if defined(_WIN32) // May not be available on all platform
1945 	err = ioctlsocket(sock, FIONREAD, &n);
1946 #else
1947 	err = ioctl(sock, FIONREAD, &n);
1948 #endif
1949 	if (err < 0)
1950 		return 0;
1951 
1952 	if (udpBufferSize > 0 && n > 0) {
1953 		// TODO: Cap number of bytes of full DGRAM message(s) up to buffer size, but may cause Warriors Orochi 2 to get FPS drops
1954 	}
1955 	return n;
1956 }
1957 
getSockMaxSize(int udpsock)1958 int getSockMaxSize(int udpsock) {
1959 	int n = PSP_ADHOC_PDP_MTU; // Typical MTU size is 1500
1960 #if defined(SO_MAX_MSG_SIZE) // May not be available on all platform
1961 	socklen_t m = sizeof(n);
1962 	getsockopt(udpsock, SOL_SOCKET, SO_MAX_MSG_SIZE, (char*)&n, &m);
1963 #endif
1964 	return n;
1965 }
1966 
getSockBufferSize(int sock,int opt)1967 int getSockBufferSize(int sock, int opt) { // opt = SO_RCVBUF/SO_SNDBUF
1968 	int n = PSP_ADHOC_PDP_MFS; // 16384;
1969 	socklen_t m = sizeof(n);
1970 	getsockopt(sock, SOL_SOCKET, opt, (char *)&n, &m); // in linux the value is twice of the value being set using setsockopt
1971 	return (n/2);
1972 }
1973 
setSockBufferSize(int sock,int opt,int size)1974 int setSockBufferSize(int sock, int opt, int size) { // opt = SO_RCVBUF/SO_SNDBUF
1975 	int n = size; // 8192; //16384
1976 	return setsockopt(sock, SOL_SOCKET, opt, (char *)&n, sizeof(n));
1977 }
1978 
setSockMSS(int sock,int size)1979 int setSockMSS(int sock, int size) {
1980 	int mss = size; // 1460;
1981 	return setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, (char*)&mss, sizeof(mss));
1982 }
1983 
setSockTimeout(int sock,int opt,unsigned long timeout_usec)1984 int setSockTimeout(int sock, int opt, unsigned long timeout_usec) { // opt = SO_SNDTIMEO/SO_RCVTIMEO
1985 	if (timeout_usec > 0 && timeout_usec < minSocketTimeoutUS) timeout_usec = minSocketTimeoutUS; // Override timeout for high latency multiplayer
1986 #if defined(_WIN32)
1987 	unsigned long optval = timeout_usec / 1000UL;
1988 	if (timeout_usec > 0 && optval == 0) optval = 1; // Since there are games that use 100 usec timeout, we should set it to minimum value on Windows (1 msec) instead of using 0 (0 = indefinitely timeout)
1989 #elif defined(__APPLE__)
1990 	struct timeval optval;
1991 	optval.tv_sec = static_cast<long>(timeout_usec) / 1000000L;
1992 	optval.tv_usec = static_cast<long>(timeout_usec) % 1000000L;
1993 #else
1994 	struct timeval optval = { static_cast<long>(timeout_usec) / 1000000L, static_cast<long>(timeout_usec) % 1000000L };
1995 #endif
1996 	return setsockopt(sock, SOL_SOCKET, opt, (char*)&optval, sizeof(optval));
1997 }
1998 
getSockNoDelay(int tcpsock)1999 int getSockNoDelay(int tcpsock) {
2000 	int opt = 0;
2001 	socklen_t optlen = sizeof(opt);
2002 	getsockopt(tcpsock, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, &optlen);
2003 	return opt;
2004 }
2005 
2006 //#define TCP_QUICKACK     0x0c
setSockNoDelay(int tcpsock,int flag)2007 int setSockNoDelay(int tcpsock, int flag) {
2008 	int opt = flag;
2009 	// Disable ACK Delay when supported
2010 #if defined(TCP_QUICKACK)
2011 	setsockopt(tcpsock, IPPROTO_TCP, TCP_QUICKACK, (char*)&opt, sizeof(opt));
2012 #elif defined(_WIN32)
2013 #if !defined(SIO_TCP_SET_ACK_FREQUENCY)
2014 	#define SIO_TCP_SET_ACK_FREQUENCY _WSAIOW(IOC_VENDOR,23)
2015 #endif
2016 	int freq = flag? 1:2; // can be 1..255, default is 2 (delayed 200ms)
2017 	DWORD retbytes = 0;
2018 	WSAIoctl(tcpsock, SIO_TCP_SET_ACK_FREQUENCY, &freq, sizeof(freq), NULL, 0, &retbytes, NULL, NULL);
2019 #endif
2020 	// Disable Nagle Algo
2021 	return setsockopt(tcpsock, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, sizeof(opt));
2022 }
2023 
setSockNoSIGPIPE(int sock,int flag)2024 int setSockNoSIGPIPE(int sock, int flag) {
2025 	// Set SIGPIPE when supported (ie. BSD/MacOS X)
2026 	int opt = flag;
2027 #if defined(SO_NOSIGPIPE)
2028 	return setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&opt, sizeof(opt));
2029 #endif
2030 	return -1;
2031 }
2032 
setSockReuseAddrPort(int sock)2033 int setSockReuseAddrPort(int sock) {
2034 	int opt = 1;
2035 	// Should we set SO_BROADCAST too for SO_REUSEADDR to works like SO_REUSEPORT ?
2036 	// Set SO_REUSEPORT also when supported (ie. Android)
2037 #if defined(SO_REUSEPORT)
2038 	setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&opt, sizeof(opt));
2039 #endif
2040 	return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
2041 }
2042 
setUDPConnReset(int udpsock,bool enabled)2043 int setUDPConnReset(int udpsock, bool enabled) {
2044 	// On Windows: Connection Reset error on UDP could cause a strange behavior https://stackoverflow.com/questions/34242622/windows-udp-sockets-recvfrom-fails-with-error-10054
2045 #if defined(_WIN32)
2046 #if !defined(SIO_UDP_CONNRESET)
2047 #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12)
2048 #endif
2049 	BOOL bNewBehavior = enabled;
2050 	DWORD dwBytesReturned = 0;
2051 	return WSAIoctl(udpsock, SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL);
2052 #endif
2053 	return -1;
2054 }
2055 
2056 #if !defined(TCP_KEEPIDLE) && !PPSSPP_PLATFORM(SWITCH)
2057 #define TCP_KEEPIDLE	TCP_KEEPALIVE //TCP_KEEPIDLE on Linux is equivalent to TCP_KEEPALIVE on macOS
2058 #endif
2059 // VS 2017 compatibility
2060 #if _MSC_VER
2061 #ifndef TCP_KEEPCNT
2062 #define TCP_KEEPCNT 16
2063 #endif
2064 #ifndef TCP_KEEPINTVL
2065 #define TCP_KEEPINTVL 17
2066 #endif
2067 #endif
setSockKeepAlive(int sock,bool keepalive,const int keepinvl,const int keepcnt,const int keepidle)2068 int setSockKeepAlive(int sock, bool keepalive, const int keepinvl, const int keepcnt, const int keepidle) {
2069 	int optval = keepalive ? 1 : 0;
2070 	int optlen = sizeof(optval);
2071 	int result = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, optlen);
2072 #if !PPSSPP_PLATFORM(SWITCH)
2073 	if (result == 0 && keepalive) {
2074 		if (getsockopt(sock, SOL_SOCKET, SO_TYPE, (char*)&optval, (socklen_t*)&optlen) == 0 && optval == SOCK_STREAM) {
2075 			optlen = sizeof(optval);
2076 			optval = keepidle; //180 sec
2077 			setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&optval, optlen);
2078 			optval = keepinvl; //60 sec
2079 			setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&optval, optlen);
2080 			optval = keepcnt; //20
2081 			setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (char*)&optval, optlen);
2082 		}
2083 	}
2084 #endif // !PPSSPP_PLATFORM(SWITCH)
2085 	return result;
2086 }
2087 
2088 /**
2089 * Return the Number of Players with the chosen Nickname in the Local Users current Network
2090 * @param nickname To-be-searched Nickname
2091 * @return Number of matching Players
2092 */
getNicknameCount(const char * nickname)2093 int getNicknameCount(const char * nickname)
2094 {
2095 	// Counter
2096 	int count = 0;
2097 
2098 	// Local Nickname Matches
2099 	if (strncmp((char *)&parameter.nickname.data, nickname, ADHOCCTL_NICKNAME_LEN) == 0) count++;
2100 
2101 	// Peer Reference
2102 	SceNetAdhocctlPeerInfo * peer = friends;
2103 
2104 	// Iterate Peers
2105 	for (; peer != NULL; peer = peer->next)
2106 	{
2107 		// Match found
2108 		if (peer->last_recv != 0 && strncmp((char *)&peer->nickname.data, nickname, ADHOCCTL_NICKNAME_LEN) == 0) count++;
2109 	}
2110 
2111 	// Return Result
2112 	return count;
2113 }
2114 
2115 /**
2116 * PDP Socket Counter
2117 * @return Number of internal PDP Sockets
2118 */
getPDPSocketCount()2119 int getPDPSocketCount()
2120 {
2121 	// Socket Counter
2122 	int counter = 0;
2123 
2124 	// Count Sockets
2125 	for (int i = 0; i < MAX_SOCKET; i++)
2126 		if (adhocSockets[i] != NULL && adhocSockets[i]->type == SOCK_PDP)
2127 			counter++;
2128 
2129 	// Return Socket Count
2130 	return counter;
2131 }
2132 
getPTPSocketCount()2133 int getPTPSocketCount() {
2134 	// Socket Counter
2135 	int counter = 0;
2136 
2137 	// Count Sockets
2138 	for (int i = 0; i < MAX_SOCKET; i++)
2139 		if (adhocSockets[i] != NULL && adhocSockets[i]->type == SOCK_PTP)
2140 			counter++;
2141 
2142 	// Return Socket Count
2143 	return counter;
2144 }
2145 
initNetwork(SceNetAdhocctlAdhocId * adhoc_id)2146 int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){
2147 	auto n = GetI18NCategory("Networking");
2148 	int iResult = 0;
2149 	metasocket = (int)INVALID_SOCKET;
2150 	metasocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
2151 	if (metasocket == INVALID_SOCKET){
2152 		ERROR_LOG(SCENET, "Invalid socket");
2153 		return SOCKET_ERROR;
2154 	}
2155 	setSockKeepAlive((int)metasocket, true);
2156 	// Disable Nagle Algo to prevent delaying small packets
2157 	setSockNoDelay((int)metasocket, 1);
2158 	// Switch to Nonblocking Behaviour
2159 	changeBlockingMode((int)metasocket, 1);
2160 
2161 	// If Server is at localhost Try to Bind socket to specific adapter before connecting to prevent 2nd instance being recognized as already existing 127.0.0.1 by AdhocServer
2162 	// (may not works in WinXP/2003 for IPv4 due to "Weak End System" model)
2163 	if (isLoopbackIP(g_adhocServerIP.in.sin_addr.s_addr)) {
2164 		int on = 1;
2165 		// Not sure what is this SO_DONTROUTE supposed to fix, but i do remembered there were issue related to multiple-instances without SO_DONTROUTE, but forgot how to reproduce it :(
2166 		setsockopt((int)metasocket, SOL_SOCKET, SO_DONTROUTE, (const char*)&on, sizeof(on));
2167 		setSockReuseAddrPort((int)metasocket);
2168 
2169 		g_localhostIP.in.sin_port = 0;
2170 		// Bind Local Address to Socket
2171 		iResult = bind((int)metasocket, &g_localhostIP.addr, sizeof(g_localhostIP.addr));
2172 		if (iResult == SOCKET_ERROR) {
2173 			ERROR_LOG(SCENET, "Bind to alternate localhost[%s] failed(%i).", ip2str(g_localhostIP.in.sin_addr).c_str(), iResult);
2174 			host->NotifyUserMessage(std::string(n->T("Failed to Bind Localhost IP")) + " " + ip2str(g_localhostIP.in.sin_addr).c_str(), 2.0, 0x0000ff);
2175 		}
2176 	}
2177 
2178 	// Default/Initial Network Parameters
2179 	memset(&parameter, 0, sizeof(parameter));
2180 	strncpy((char *)&parameter.nickname.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN);
2181 	parameter.nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;
2182 	parameter.channel = g_Config.iWlanAdhocChannel;
2183 	// Assign a Valid Channel when connected to AP/Adhoc if it's Auto. JPCSP use 11 as default for Auto (Commonly for Auto: 1, 6, 11)
2184 	if (parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) parameter.channel = defaultWlanChannel; // Faked Active channel to default channel
2185 	//getLocalMac(&parameter.bssid.mac_addr);
2186 
2187 	// Default ProductId
2188 	product_code.type = adhoc_id->type;
2189 	memcpy(product_code.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN);
2190 
2191 	// Don't need to connect if AdhocServer DNS was not resolved
2192 	if (g_adhocServerIP.in.sin_addr.s_addr == INADDR_NONE)
2193 		return -1;
2194 
2195 	// Don't need to connect if AdhocServer IP is the same with this instance localhost IP and having AdhocServer disabled
2196 	if (g_adhocServerIP.in.sin_addr.s_addr == g_localhostIP.in.sin_addr.s_addr && !g_Config.bEnableAdhocServer)
2197 		return -1;
2198 
2199 	// Connect to Adhoc Server
2200 	int errorcode = 0;
2201 	int cnt = 0;
2202 	iResult = connect((int)metasocket, &g_adhocServerIP.addr, sizeof(g_adhocServerIP));
2203 	errorcode = errno;
2204 
2205 	if (iResult == SOCKET_ERROR && errorcode != EISCONN) {
2206 		u64 startTime = (u64)(time_now_d() * 1000000.0);
2207 		while (IsSocketReady((int)metasocket, false, true) <= 0) {
2208 			u64 now = (u64)(time_now_d() * 1000000.0);
2209 			if (coreState == CORE_POWERDOWN) return iResult;
2210 			if (now - startTime > adhocDefaultTimeout) break;
2211 			sleep_ms(10);
2212 		}
2213 		if (IsSocketReady((int)metasocket, false, true) <= 0) {
2214 			ERROR_LOG(SCENET, "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.proAdhocServer.c_str(), ip2str(g_adhocServerIP.in.sin_addr).c_str(), ntohs(g_adhocServerIP.in.sin_port));
2215 			host->NotifyUserMessage(std::string(n->T("Failed to connect to Adhoc Server")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(errorcode) + ")", 1.0f, 0x0000ff);
2216 			return iResult;
2217 		}
2218 	}
2219 
2220 	// Prepare Login Packet
2221 	SceNetAdhocctlLoginPacketC2S packet;
2222 	packet.base.opcode = OPCODE_LOGIN;
2223 	SceNetEtherAddr addres;
2224 	getLocalMac(&addres);
2225 	packet.mac = addres;
2226 	strncpy((char *)&packet.name.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN);
2227 	packet.name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;
2228 	memcpy(packet.game.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN);
2229 
2230 	IsSocketReady((int)metasocket, false, true, nullptr, adhocDefaultTimeout);
2231 	int sent = send((int)metasocket, (char*)&packet, sizeof(packet), MSG_NOSIGNAL);
2232 	if (sent > 0) {
2233 		socklen_t addrLen = sizeof(LocalIP);
2234 		memset(&LocalIP, 0, addrLen);
2235 		getsockname((int)metasocket, &LocalIP, &addrLen);
2236 		host->NotifyUserMessage(n->T("Network Initialized"), 1.0);
2237 		return 0;
2238 	}
2239 	else{
2240 		return SOCKET_ERROR;
2241 	}
2242 }
2243 
isZeroMAC(const SceNetEtherAddr * addr)2244 bool isZeroMAC(const SceNetEtherAddr* addr) {
2245 	return (memcmp(addr->data, "\x00\x00\x00\x00\x00\x00", ETHER_ADDR_LEN) == 0);
2246 }
2247 
isBroadcastMAC(const SceNetEtherAddr * addr)2248 bool isBroadcastMAC(const SceNetEtherAddr * addr) {
2249 	return (memcmp(addr->data, "\xFF\xFF\xFF\xFF\xFF\xFF", ETHER_ADDR_LEN) == 0);
2250 }
2251 
resolveIP(uint32_t ip,SceNetEtherAddr * mac)2252 bool resolveIP(uint32_t ip, SceNetEtherAddr * mac) {
2253 	sockaddr_in addr;
2254 	getLocalIp(&addr);
2255 	uint32_t localIp = addr.sin_addr.s_addr;
2256 
2257 	if (ip == localIp || ip == g_localhostIP.in.sin_addr.s_addr) {
2258 		getLocalMac(mac);
2259 		return true;
2260 	}
2261 
2262 	// Multithreading Lock
2263 	std::lock_guard<std::recursive_mutex> peer_guard(peerlock);
2264 
2265 	// Peer Reference
2266 	SceNetAdhocctlPeerInfo * peer = friends;
2267 
2268 	// Iterate Peers
2269 	for (; peer != NULL; peer = peer->next) {
2270 		// Found Matching Peer
2271 		if (peer->ip_addr == ip) {
2272 			// Copy Data
2273 			*mac = peer->mac_addr;
2274 
2275 			// Return Success
2276 			return true;
2277 		}
2278 	}
2279 
2280 	// Peer not found
2281 	return false;
2282 }
2283 
resolveMAC(SceNetEtherAddr * mac,uint32_t * ip)2284 bool resolveMAC(SceNetEtherAddr * mac, uint32_t * ip) {
2285 	// Get Local MAC Address
2286 	SceNetEtherAddr localMac;
2287 	getLocalMac(&localMac);
2288 	// Local MAC Requested
2289 	if (isMacMatch(&localMac, mac)) {
2290 		// Get Local IP Address
2291 		sockaddr_in sockAddr;
2292 		getLocalIp(&sockAddr);
2293 		*ip = sockAddr.sin_addr.s_addr;
2294 		return true; // return succes
2295 	}
2296 
2297 	// Multithreading Lock
2298 	std::lock_guard<std::recursive_mutex> peer_guard(peerlock);
2299 
2300 	// Peer Reference
2301 	SceNetAdhocctlPeerInfo * peer = friends;
2302 
2303 	// Iterate Peers
2304 	for (; peer != NULL; peer = peer->next) {
2305 		// Found Matching Peer
2306 		if (isMacMatch(&peer->mac_addr, mac)) {
2307 			// Copy Data
2308 			*ip = peer->ip_addr;
2309 
2310 			// Return Success
2311 			return true;
2312 		}
2313 	}
2314 
2315 	// Peer not found
2316 	return false;
2317 }
2318 
validNetworkName(const SceNetAdhocctlGroupName * group_name)2319 bool validNetworkName(const SceNetAdhocctlGroupName * group_name) {
2320 	// Result
2321 	bool valid = true;
2322 
2323 	// Name given
2324 	if (group_name != NULL) {
2325 		// Iterate Name Characters
2326 		for (int i = 0; i < ADHOCCTL_GROUPNAME_LEN && valid; i++) {
2327 			// End of Name
2328 			if (group_name->data[i] == 0) break;
2329 
2330 			// Not a digit
2331 			if (group_name->data[i] < '0' || group_name->data[i] > '9') {
2332 				// Not 'A' to 'Z'
2333 				if (group_name->data[i] < 'A' || group_name->data[i] > 'Z') {
2334 					// Not 'a' to 'z'
2335 					if (group_name->data[i] < 'a' || group_name->data[i] > 'z') {
2336 						// Invalid Name
2337 						valid = false;
2338 					}
2339 				}
2340 			}
2341 		}
2342 	}
2343 	// Return Result
2344 	return valid;
2345 }
2346 
join32(u32 num1,u32 num2)2347 u64 join32(u32 num1, u32 num2){
2348 	return (u64)num2 << 32 | num1;
2349 }
2350 
split64(u64 num,int buff[])2351 void split64(u64 num, int buff[]){
2352 	int num1 = (int)(num&firstMask);
2353 	int num2 = (int)((num&secondMask) >> 32);
2354 	buff[0] = num1;
2355 	buff[1] = num2;
2356 }
2357 
getMatchingEventStr(int code)2358 const char* getMatchingEventStr(int code) {
2359 	const char *buf = NULL;
2360 	switch (code) {
2361 	case PSP_ADHOC_MATCHING_EVENT_HELLO:
2362 		buf = "HELLO"; break;
2363 	case PSP_ADHOC_MATCHING_EVENT_REQUEST:
2364 		buf = "JOIN"; break;
2365 	case PSP_ADHOC_MATCHING_EVENT_LEAVE:
2366 		buf = "LEAVE"; break;
2367 	case PSP_ADHOC_MATCHING_EVENT_DENY:
2368 		buf = "REJECT"; break;
2369 	case PSP_ADHOC_MATCHING_EVENT_CANCEL:
2370 		buf = "CANCEL"; break;
2371 	case PSP_ADHOC_MATCHING_EVENT_ACCEPT:
2372 		buf = "ACCEPT"; break;
2373 	case PSP_ADHOC_MATCHING_EVENT_ESTABLISHED:
2374 		buf = "ESTABLISHED"; break;
2375 	case PSP_ADHOC_MATCHING_EVENT_TIMEOUT:
2376 		buf = "TIMEOUT"; break;
2377 	case PSP_ADHOC_MATCHING_EVENT_ERROR:
2378 		buf = "ERROR"; break;
2379 	case PSP_ADHOC_MATCHING_EVENT_BYE:
2380 		buf = "DISCONNECT"; break;
2381 	case PSP_ADHOC_MATCHING_EVENT_DATA:
2382 		buf = "DATA"; break;
2383 	case PSP_ADHOC_MATCHING_EVENT_DATA_ACK:
2384 		buf = "DATA_ACK"; break;
2385 	case PSP_ADHOC_MATCHING_EVENT_DATA_TIMEOUT:
2386 		buf = "DATA_TIMEOUT"; break;
2387 	case PSP_ADHOC_MATCHING_EVENT_INTERNAL_PING:
2388 		buf = "INTERNAL_PING"; break;
2389 	default:
2390 		buf = "UNKNOWN";
2391 	}
2392 	return buf;
2393 }
2394 
getMatchingOpcodeStr(int code)2395 const char* getMatchingOpcodeStr(int code) {
2396 	const char *buf = NULL;
2397 	switch (code) {
2398 	case PSP_ADHOC_MATCHING_PACKET_PING:
2399 		buf = "PING"; break;
2400 	case PSP_ADHOC_MATCHING_PACKET_HELLO:
2401 		buf = "HELLO"; break;
2402 	case PSP_ADHOC_MATCHING_PACKET_JOIN:
2403 		buf = "JOIN"; break;
2404 	case PSP_ADHOC_MATCHING_PACKET_ACCEPT:
2405 		buf = "ACCEPT"; break;
2406 	case PSP_ADHOC_MATCHING_PACKET_CANCEL:
2407 		buf = "CANCEL"; break;
2408 	case PSP_ADHOC_MATCHING_PACKET_BULK:
2409 		buf = "BULK"; break;
2410 	case PSP_ADHOC_MATCHING_PACKET_BULK_ABORT:
2411 		buf = "BULK_ABORT"; break;
2412 	case PSP_ADHOC_MATCHING_PACKET_BIRTH:
2413 		buf = "BIRTH"; break;
2414 	case PSP_ADHOC_MATCHING_PACKET_DEATH:
2415 		buf = "DEATH"; break;
2416 	case PSP_ADHOC_MATCHING_PACKET_BYE:
2417 		buf = "BYE"; break;
2418 	default:
2419 		buf = "UNKNOWN";
2420 	}
2421 	return buf;
2422 }
2423 
2424