CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/HLE/proAdhoc.cpp
Views: 1401
// Copyright (c) 2013- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.161718// proAdhoc1920// This is a direct port of Coldbird's code from http://code.google.com/p/aemu/21// All credit goes to him!2223#include "ppsspp_config.h"2425#if defined(_WIN32)26#include <WinSock2.h>27#include "Common/CommonWindows.h"28#endif2930#if !defined(_WIN32)31#include <unistd.h>32#include <netinet/tcp.h>33#include <sys/ioctl.h>34#include <sys/socket.h>35#include <sys/types.h>36#if !PPSSPP_PLATFORM(SWITCH)37#include <ifaddrs.h>38#endif // !PPSSPP_PLATFORM(SWITCH)39#endif4041#ifndef MSG_NOSIGNAL42// Default value to 0x00 (do nothing) in systems where it's not supported.43#define MSG_NOSIGNAL 0x0044#endif4546#if defined(HAVE_LIBNX) || PPSSPP_PLATFORM(SWITCH)47#undef __BSD_VISIBLE48#define __BSD_VISIBLE 149#include <switch.h>50#define TCP_MAXSEG 251#endif // defined(HAVE_LIBNX) || PPSSPP_PLATFORM(SWITCH)5253#include <algorithm>54#include <mutex>55#include <cstring>5657#include "Common/Data/Text/I18n.h"58#include "Common/Data/Text/Parsers.h"59#include "Common/System/OSD.h"60#include "Common/Thread/ThreadUtil.h"6162#include "Common/Serialize/SerializeFuncs.h"63#include "Common/TimeUtil.h"6465#include "Core/HLE/sceKernelThread.h"66#include "Core/HLE/sceKernel.h"67#include "Core/HLE/sceKernelMutex.h"68#include "Core/HLE/sceUtility.h"6970#include "Core/MemMap.h"71#include "Core/HLE/HLE.h"72#include "Core/HLE/HLEHelperThread.h"73#include "Core/Config.h"74#include "Core/CoreTiming.h"75#include "Core/Core.h"76#include "Core/HLE/sceKernelInterrupt.h"77#include "Core/HLE/sceKernelThread.h"78#include "Core/HLE/sceKernelMemory.h"79#include "Core/HLE/sceNetAdhoc.h"80#include "Core/Instance.h"81#include "proAdhoc.h"8283#if PPSSPP_PLATFORM(SWITCH) && !defined(INADDR_NONE)84// Missing toolchain define85#define INADDR_NONE 0xFFFFFFFF86#endif8788uint16_t portOffset;89uint32_t minSocketTimeoutUS;90uint32_t fakePoolSize = 0;91SceNetAdhocMatchingContext * contexts = NULL;92char* dummyPeekBuf64k = NULL;93int dummyPeekBuf64kSize = 65536;94int one = 1;95std::atomic<bool> friendFinderRunning(false);96SceNetAdhocctlPeerInfo * friends = NULL;97SceNetAdhocctlScanInfo * networks = NULL;98SceNetAdhocctlScanInfo * newnetworks = NULL;99u64 adhocctlStartTime = 0;100bool isAdhocctlNeedLogin = false;101bool isAdhocctlBusy = false;102int adhocctlState = ADHOCCTL_STATE_DISCONNECTED;103int adhocctlCurrentMode = ADHOCCTL_MODE_NONE;104int adhocConnectionType = ADHOC_CONNECT;105106int gameModeSocket = (int)INVALID_SOCKET; // UDP/PDP socket? on Master only?107int gameModeBuffSize = 0;108u8* gameModeBuffer = nullptr;109GameModeArea masterGameModeArea;110std::vector<GameModeArea> replicaGameModeAreas;111std::vector<SceNetEtherAddr> requiredGameModeMacs;112std::vector<SceNetEtherAddr> gameModeMacs;113std::map<SceNetEtherAddr, u16_le> gameModePeerPorts;114115int actionAfterAdhocMipsCall;116int actionAfterMatchingMipsCall;117118// Broadcast MAC119uint8_t broadcastMAC[ETHER_ADDR_LEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };120121std::atomic<int> metasocket((int)INVALID_SOCKET);122SceNetAdhocctlParameter parameter;123SceNetAdhocctlAdhocId product_code;124std::thread friendFinderThread;125std::recursive_mutex peerlock;126AdhocSocket* adhocSockets[MAX_SOCKET];127bool isOriPort = false;128bool isLocalServer = false;129SockAddrIN4 g_adhocServerIP;130SockAddrIN4 g_localhostIP;131sockaddr LocalIP;132int defaultWlanChannel = PSP_SYSTEMPARAM_ADHOC_CHANNEL_11; // Don't put 0(Auto) here, it needed to be a valid/actual channel number133134static std::mutex chatLogLock;135static std::vector<std::string> chatLog;136static int chatMessageGeneration = 0;137static int chatMessageCount = 0;138139bool isMacMatch(const SceNetEtherAddr* addr1, const SceNetEtherAddr* addr2) {140// Ignoring the 1st byte since there are games (ie. Gran Turismo) who tamper with the 1st byte of OUI to change the unicast/multicast bit141return (memcmp(((const char*)addr1)+1, ((const char*)addr2)+1, ETHER_ADDR_LEN-1) == 0);142}143144bool isLocalMAC(const SceNetEtherAddr * addr) {145SceNetEtherAddr saddr;146getLocalMac(&saddr);147148return isMacMatch(addr, &saddr);149}150151bool isPDPPortInUse(uint16_t port) {152// Iterate Elements153for (int i = 0; i < MAX_SOCKET; i++) {154auto sock = adhocSockets[i];155if (sock != NULL && sock->type == SOCK_PDP)156if (sock->data.pdp.lport == port)157return true;158}159// Unused Port160return false;161}162163bool isPTPPortInUse(uint16_t port, bool forListen, SceNetEtherAddr* dstmac, uint16_t dstport) {164// Iterate Sockets165for (int i = 0; i < MAX_SOCKET; i++) {166auto sock = adhocSockets[i];167if (sock != NULL && sock->type == SOCK_PTP)168// 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).169if (sock->data.ptp.lport == port &&170((forListen && sock->data.ptp.state == ADHOC_PTP_STATE_LISTEN) ||171(!forListen && sock->data.ptp.state != ADHOC_PTP_STATE_LISTEN &&172sock->data.ptp.pport == dstport && dstmac != nullptr && isMacMatch(&sock->data.ptp.paddr, dstmac))))173{174return true;175}176}177// Unused Port178return false;179}180181// Replacement for inet_ntoa since it's getting deprecated182std::string ip2str(in_addr in, bool maskPublicIP) {183char str[INET_ADDRSTRLEN] = "...";184u8* ipptr = (u8*)∈185if (maskPublicIP && !isPrivateIP(in.s_addr))186snprintf(str, sizeof(str), "%u.%u.xx.%u", ipptr[0], ipptr[1], ipptr[3]);187else188snprintf(str, sizeof(str), "%u.%u.%u.%u", ipptr[0], ipptr[1], ipptr[2], ipptr[3]);189return std::string(str);190}191192std::string mac2str(const SceNetEtherAddr *mac) {193char str[18] = ":::::";194195if (mac != NULL) {196snprintf(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]);197}198199return std::string(str);200}201202SceNetAdhocMatchingMemberInternal* addMember(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac) {203if (context == NULL || mac == NULL) return NULL;204205SceNetAdhocMatchingMemberInternal * peer = findPeer(context, mac);206// Already existed207if (peer != NULL) {208WARN_LOG(Log::sceNet, "Member Peer Already Existed! Updating [%s]", mac2str(mac).c_str());209peer->state = 0;210peer->sending = 0;211peer->lastping = CoreTiming::GetGlobalTimeUsScaled();212}213// Member is not added yet214else {215peer = (SceNetAdhocMatchingMemberInternal *)malloc(sizeof(SceNetAdhocMatchingMemberInternal));216if (peer != NULL) {217memset(peer, 0, sizeof(SceNetAdhocMatchingMemberInternal));218peer->mac = *mac;219peer->lastping = CoreTiming::GetGlobalTimeUsScaled();220peerlock.lock();221peer->next = context->peerlist;222context->peerlist = peer;223peerlock.unlock();224}225}226return peer;227}228229void addFriend(SceNetAdhocctlConnectPacketS2C * packet) {230if (packet == NULL) return;231232// Multithreading Lock233std::lock_guard<std::recursive_mutex> guard(peerlock);234235SceNetAdhocctlPeerInfo * peer = findFriend(&packet->mac);236// Already existed237if (peer != NULL) {238u32 tmpip = packet->ip;239WARN_LOG(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)240peer->nickname = packet->name;241peer->mac_addr = packet->mac;242peer->ip_addr = packet->ip;243// Calculate final IP-specific Port Offset244peer->port_offset = ((isOriPort && !isPrivateIP(peer->ip_addr)) ? 0 : portOffset);245// Update TimeStamp246peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();247}248else {249// Allocate Structure250peer = (SceNetAdhocctlPeerInfo *)malloc(sizeof(SceNetAdhocctlPeerInfo));251// Allocated Structure252if (peer != NULL) {253// Clear Memory254memset(peer, 0, sizeof(SceNetAdhocctlPeerInfo));255256// Save Nickname257peer->nickname = packet->name;258259// Save MAC Address260peer->mac_addr = packet->mac;261262// Save IP Address263peer->ip_addr = packet->ip;264265// Calculate final IP-specific Port Offset266peer->port_offset = ((isOriPort && !isPrivateIP(peer->ip_addr)) ? 0 : portOffset);267268// TimeStamp269peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();270271// Link to existing Peers272peer->next = friends;273274// Link into Peerlist275friends = peer;276}277}278}279280SceNetAdhocctlPeerInfo * findFriend(SceNetEtherAddr * MAC) {281if (MAC == NULL) return NULL;282283// Friends Reference284SceNetAdhocctlPeerInfo * peer = friends;285286// Iterate Friends287for (; peer != NULL; peer = peer->next) {288if (isMacMatch(&peer->mac_addr, MAC)) break;289}290291// Return found friend292return peer;293}294295SceNetAdhocctlPeerInfo* findFriendByIP(uint32_t ip) {296// Friends Reference297SceNetAdhocctlPeerInfo* peer = friends;298299// Iterate Friends300for (; peer != NULL; peer = peer->next) {301if (peer->ip_addr == ip) break;302}303304// Return found friend305return peer;306}307308int IsSocketReady(int fd, bool readfd, bool writefd, int* errorcode, int timeoutUS) {309fd_set readfds, writefds;310timeval tval;311312// Avoid getting Fatal signal 6 (SIGABRT) on linux/android313if (fd < 0) {314if (errorcode != nullptr)315*errorcode = EBADF;316return SOCKET_ERROR;317}318#if !defined(_WIN32)319if (fd >= FD_SETSIZE) {320if (errorcode != nullptr)321*errorcode = EBADF;322return SOCKET_ERROR;323}324#endif325326FD_ZERO(&readfds);327writefds = readfds;328if (readfd) {329FD_SET(fd, &readfds);330}331if (writefd) {332FD_SET(fd, &writefds);333}334tval.tv_sec = timeoutUS / 1000000;335tval.tv_usec = timeoutUS % 1000000;336337// Note: select will flags an unconnected TCP socket (ie. a freshly created socket without connecting first, or when connect failed with ECONNREFUSED on linux) as writeable/readable, thus can't be used to tell whether the connection has established or not.338int ret = select(fd + 1, readfd? &readfds: nullptr, writefd? &writefds: nullptr, nullptr, &tval);339if (errorcode != nullptr)340*errorcode = (ret < 0? errno: 0);341342return ret;343}344345void changeBlockingMode(int fd, int nonblocking) {346unsigned long on = 1;347unsigned long off = 0;348#if defined(_WIN32)349if (nonblocking) {350// Change to Non-Blocking Mode351ioctlsocket(fd, FIONBIO, &on);352}353else {354// Change to Blocking Mode355ioctlsocket(fd, FIONBIO, &off);356}357// If they have O_NONBLOCK, use the POSIX way to do it. On POSIX sockets Error code would be EINPROGRESS instead of EAGAIN358//#elif defined(O_NONBLOCK)359#else360int flags = fcntl(fd, F_GETFL, 0);361// Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5.362if (flags == -1)363flags = 0;364if (nonblocking) {365// Set Non-Blocking Flag366fcntl(fd, F_SETFL, flags | O_NONBLOCK);367}368else {369// Remove Non-Blocking Flag370fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);371}372// Otherwise, use the old way of doing it (UNIX way). On UNIX sockets Error code would be EAGAIN instead of EINPROGRESS373/*#else374if (nonblocking) {375// Change to Non - Blocking Mode376ioctl(fd, FIONBIO, (char*)&on);377}378else {379// Change to Blocking Mode380ioctl(fd, FIONBIO, (char*)&off);381}*/382#endif383}384385int countAvailableNetworks(const bool excludeSelf) {386// Network Count387int count = 0;388389// Group Reference390SceNetAdhocctlScanInfo * group = networks;391392// Count Groups393for (; group != NULL && (!excludeSelf || !isLocalMAC(&group->bssid.mac_addr)); group = group->next) count++;394395// Return Network Count396return count;397}398399SceNetAdhocctlScanInfo * findGroup(SceNetEtherAddr * MAC) {400if (MAC == NULL) return NULL;401402// Groups Reference403SceNetAdhocctlScanInfo * group = networks;404405// Iterate Groups406for (; group != NULL; group = group->next) {407if (isMacMatch(&group->bssid.mac_addr, MAC)) break;408}409410// Return found group411return group;412}413414void freeGroupsRecursive(SceNetAdhocctlScanInfo * node) {415// End of List416if (node == NULL) return;417418// Increase Recursion Depth419freeGroupsRecursive(node->next);420421// Free Memory422free(node);423node = NULL;424}425426void deleteAllAdhocSockets() {427// Iterate Element428for (int i = 0; i < MAX_SOCKET; i++) {429// Active Socket430if (adhocSockets[i] != NULL) {431auto sock = adhocSockets[i];432int fd = -1;433434if (sock->type == SOCK_PTP)435fd = sock->data.ptp.id;436else if (sock->type == SOCK_PDP)437fd = sock->data.pdp.id;438439if (fd > 0) {440// Close Socket441struct linger sl {};442sl.l_onoff = 1; // non-zero value enables linger option in kernel443sl.l_linger = 0; // timeout interval in seconds444setsockopt(fd, SOL_SOCKET, SO_LINGER, (const char*)&sl, sizeof(sl));445shutdown(fd, SD_RECEIVE);446closesocket(fd);447}448// Free Memory449free(adhocSockets[i]);450451// Delete Reference452adhocSockets[i] = NULL;453}454}455}456457void deleteAllGMB() {458if (gameModeBuffer) {459free(gameModeBuffer);460gameModeBuffer = nullptr;461gameModeBuffSize = 0;462}463if (masterGameModeArea.data) {464free(masterGameModeArea.data);465masterGameModeArea = { 0 };466}467for (auto& it : replicaGameModeAreas) {468if (it.data) {469free(it.data);470it.data = nullptr;471}472}473replicaGameModeAreas.clear();474gameModeMacs.clear();475requiredGameModeMacs.clear();476}477478void deleteFriendByIP(uint32_t ip) {479// Previous Peer Reference480SceNetAdhocctlPeerInfo * prev = NULL;481482// Peer Pointer483SceNetAdhocctlPeerInfo * peer = friends;484485// Iterate Peers486for (; peer != NULL; peer = peer->next) {487// Found Peer488if (peer->ip_addr == ip) {489490// Multithreading Lock491peerlock.lock();492493// Unlink Left (Beginning)494/*if (prev == NULL) friends = peer->next;495496// Unlink Left (Other)497else prev->next = peer->next;498*/499500u32 tmpip = peer->ip_addr;501INFO_LOG(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)502503// Free Memory504//free(peer);505//peer = NULL;506// 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 peer507peer->last_recv = 0; //CoreTiming::GetGlobalTimeUsScaled();508509// Multithreading Unlock510peerlock.unlock();511512// Stop Search513break;514}515516// Set Previous Reference517prev = peer;518}519}520521int findFreeMatchingID() {522// Minimum Matching ID523int min = 1;524525// Maximum Matching ID526int max = 0;527528// Find highest Matching ID529SceNetAdhocMatchingContext * item = contexts;530for (; item != NULL; item = item->next) {531// New Maximum532if (max < item->id) max = item->id;533}534535// Find unoccupied ID536int i = min;537for (; i < max; i++) {538// Found unoccupied ID539if (findMatchingContext(i) == NULL) return i;540}541542// Append at virtual end543return max + 1;544}545546SceNetAdhocMatchingContext * findMatchingContext(int id) {547// Iterate Matching Context List548SceNetAdhocMatchingContext * item = contexts;549for (; item != NULL; item = item->next) { // Found Matching ID550if (item->id == id) return item;551}552553// Context not found554return NULL;555}556557/**558* Find Outgoing Request Target Peer559* @param context Matching Context Pointer560* @return Internal Peer Reference or... NULL561*/562SceNetAdhocMatchingMemberInternal * findOutgoingRequest(SceNetAdhocMatchingContext * context)563{564// Iterate Peer List for Matching Target565SceNetAdhocMatchingMemberInternal * peer = context->peerlist;566for (; peer != NULL; peer = peer->next)567{568// Found Peer in List569if (peer->state == PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST) return peer;570}571572// Peer not found573return NULL;574}575576/**577* Remove unneeded Peer Data after being accepted to a match578* @param context Matching Context Pointer579*/580void postAcceptCleanPeerList(SceNetAdhocMatchingContext * context)581{582int delcount = 0;583int peercount = 0;584// Acquire Peer Lock585peerlock.lock();586587// Iterate Peer List588SceNetAdhocMatchingMemberInternal * peer = context->peerlist;589while (peer != NULL)590{591// Save next Peer just in case we have to delete this one592SceNetAdhocMatchingMemberInternal * next = peer->next;593594// Unneeded Peer595if (peer->state != PSP_ADHOC_MATCHING_PEER_CHILD && peer->state != PSP_ADHOC_MATCHING_PEER_P2P && peer->state != PSP_ADHOC_MATCHING_PEER_PARENT && peer->state != 0) {596deletePeer(context, peer);597delcount++;598}599600// Move to Next Peer601peer = next;602peercount++;603}604605// Free Peer Lock606peerlock.unlock();607608INFO_LOG(Log::sceNet, "Removing Unneeded Peers (%i/%i)", delcount, peercount);609}610611/**612* Add Sibling-Data that was sent with Accept-Datagram613* @param context Matching Context Pointer614* @param siblingcount Number of Siblings615* @param siblings Sibling MAC Array616*/617void postAcceptAddSiblings(SceNetAdhocMatchingContext * context, int siblingcount, SceNetEtherAddr * siblings)618{619// Cast Sibling MAC Array to uint8_t620// PSP CPU has a problem with non-4-byte aligned Pointer Access.621// As the buffer of "siblings" isn't properly aligned I don't want to risk a crash.622uint8_t * siblings_u8 = (uint8_t *)siblings;623624peerlock.lock();625// Iterate Siblings. Reversed so these siblings are added into peerlist in the same order with the peerlist on host/parent side626for (int i = siblingcount - 1; i >= 0 ; i--)627{628SceNetEtherAddr* mac = (SceNetEtherAddr*)(siblings_u8 + sizeof(SceNetEtherAddr) * i);629630auto peer = findPeer(context, mac);631// Already exist632if (peer != NULL) {633// Set Peer State634peer->state = PSP_ADHOC_MATCHING_PEER_CHILD;635peer->sending = 0;636peer->lastping = CoreTiming::GetGlobalTimeUsScaled();637WARN_LOG(Log::sceNet, "Updating Sibling Peer %s", mac2str(mac).c_str());638}639else {640// Allocate Memory641SceNetAdhocMatchingMemberInternal* sibling = (SceNetAdhocMatchingMemberInternal*)malloc(sizeof(SceNetAdhocMatchingMemberInternal));642643// Allocated Memory644if (sibling != NULL)645{646// Clear Memory647memset(sibling, 0, sizeof(SceNetAdhocMatchingMemberInternal));648649// Save MAC Address650memcpy(&sibling->mac, mac, sizeof(SceNetEtherAddr));651652// Set Peer State653sibling->state = PSP_ADHOC_MATCHING_PEER_CHILD;654655// Initialize Ping Timer656sibling->lastping = CoreTiming::GetGlobalTimeUsScaled(); //time_now_d()*1000000.0;657658// Link Peer659sibling->next = context->peerlist;660context->peerlist = sibling;661662// Spawn Established Event. FIXME: ESTABLISHED event should only be triggered for Parent/P2P peer?663//spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_ESTABLISHED, &sibling->mac, 0, NULL);664665INFO_LOG(Log::sceNet, "Accepting Sibling Peer %s", mac2str(&sibling->mac).c_str());666}667}668}669peerlock.unlock();670}671672/**673* Count Children Peers (for Parent)674* @param context Matching Context Pointer675* @return Number of Children676*/677s32_le countChildren(SceNetAdhocMatchingContext * context, const bool excludeTimedout)678{679// Children Counter680s32_le count = 0;681682// Iterate Peer List for Matching Target683SceNetAdhocMatchingMemberInternal * peer = context->peerlist;684for (; peer != NULL; peer = peer->next)685{686// Exclude timedout members?687if (!excludeTimedout || peer->lastping != 0)688// Increase Children Counter689if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) count++;690}691692// Return Children Count693return count;694}695696/**697* Find Peer in Context by MAC698* @param context Matching Context Pointer699* @param mac Peer MAC Address700* @return Internal Peer Reference or... NULL701*/702SceNetAdhocMatchingMemberInternal * findPeer(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac)703{704if (mac == NULL)705return NULL;706707// Iterate Peer List for Matching Target708SceNetAdhocMatchingMemberInternal * peer = context->peerlist;709for (; peer != NULL; peer = peer->next)710{711// Found Peer in List712if (isMacMatch(&peer->mac, mac))713{714// Return Peer Pointer715return peer;716}717}718719// Peer not found720return NULL;721}722723/**724* Find Parent Peer725* @param context Matching Context Pointer726* @return Internal Peer Reference or... NULL727*/728SceNetAdhocMatchingMemberInternal * findParent(SceNetAdhocMatchingContext * context)729{730// Iterate Peer List for Matching Target731SceNetAdhocMatchingMemberInternal * peer = context->peerlist;732for (; peer != NULL; peer = peer->next)733{734// Found Peer in List735if (peer->state == PSP_ADHOC_MATCHING_PEER_PARENT) return peer;736}737738// Peer not found739return NULL;740}741742/**743* Find P2P Buddy Peer744* @param context Matching Context Pointer745* @return Internal Peer Reference or... NULL746*/747SceNetAdhocMatchingMemberInternal * findP2P(SceNetAdhocMatchingContext * context, const bool excludeTimedout)748{749// Iterate Peer List for Matching Target750SceNetAdhocMatchingMemberInternal * peer = context->peerlist;751for (; peer != NULL; peer = peer->next)752{753// Exclude timedout members?754if (!excludeTimedout || peer->lastping != 0)755// Found Peer in List756if (peer->state == PSP_ADHOC_MATCHING_PEER_P2P) return peer;757}758759// Peer not found760return NULL;761}762763/**764* Delete Peer from List765* @param context Matching Context Pointer766* @param peer Internal Peer Reference767*/768void deletePeer(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal *& peer)769{770// Valid Arguments771if (context != NULL && peer != NULL)772{773peerlock.lock();774775// Previous Peer Reference776SceNetAdhocMatchingMemberInternal * previous = NULL;777778// Iterate Peer List779SceNetAdhocMatchingMemberInternal * item = context->peerlist;780for (; item != NULL; item = item->next)781{782// Found Peer Match783if (item == peer) break;784785// Set Previous Peer786previous = item;787}788789if (item != NULL) {790// Middle Item791if (previous != NULL) previous->next = item->next;792793// Beginning Item794else context->peerlist = item->next;795796INFO_LOG(Log::sceNet, "Removing Member Peer %s", mac2str(&peer->mac).c_str());797}798799// Free Peer Memory800free(peer);801peer = NULL;802803peerlock.unlock();804}805}806807/**808* Safely Link Thread Message to Event Thread Stack809* @param context Matching Context Pointer810* @param message Thread Message Pointer811*/812void linkEVMessage(SceNetAdhocMatchingContext * context, ThreadMessage * message)813{814// Lock Access815context->eventlock->lock();816817// Link Message818message->next = context->event_stack;819context->event_stack = message;820821// Unlock Access822context->eventlock->unlock();823}824825/**826* Safely Link Thread Message to IO Thread Stack827* @param context Matching Context Pointer828* @param message Thread Message Pointer829*/830void linkIOMessage(SceNetAdhocMatchingContext * context, ThreadMessage * message)831{832// Lock Access833context->inputlock->lock();834835// Link Message836message->next = context->input_stack;837context->input_stack = message;838839// Unlock Access840context->inputlock->unlock();841}842843/**844* Send Generic Thread Message845* @param context Matching Context Pointer846* @param stack ADHOC_MATCHING_EVENT_STACK or ADHOC_MATCHING_INPUT_STACK847* @param mac Target MAC848* @param opcode Message Opcode849* @param optlen Optional Data Length850* @param opt Optional Data851*/852void sendGenericMessage(SceNetAdhocMatchingContext * context, int stack, SceNetEtherAddr * mac, int opcode, int optlen, const void * opt)853{854// Calculate Required Memory Size855uint32_t size = sizeof(ThreadMessage) + optlen;856857// Allocate Memory858uint8_t * memory = (uint8_t *)malloc(size);859860// Allocated Memory861if (memory != NULL)862{863// Clear Memory864memset(memory, 0, size);865866// Cast Header867ThreadMessage * header = (ThreadMessage *)memory;868869// Set Message Opcode870header->opcode = opcode;871872// Set Peer MAC Address873header->mac = *mac;874875// Set Optional Data Length876header->optlen = optlen;877878// Set Optional Data879memcpy(memory + sizeof(ThreadMessage), opt, optlen);880881// Link Thread Message882if (stack == PSP_ADHOC_MATCHING_EVENT_STACK) linkEVMessage(context, header);883884// Link Thread Message to Input Stack885else linkIOMessage(context, header);886887// Exit Function888return;889}890891peerlock.lock();892// Out of Memory Emergency Delete893auto peer = findPeer(context, mac);894deletePeer(context, peer);895peerlock.unlock();896}897898/**899* Send Accept Message from P2P -> P2P or Parent -> Children900* @param context Matching Context Pointer901* @param peer Target Peer902* @param optlen Optional Data Length903* @param opt Optional Data904*/905void sendAcceptMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer, int optlen, const void * opt)906{907// Send Accept Message908sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_ACCEPT, optlen, opt);909}910911/**912* Send Join Request from P2P -> P2P or Children -> Parent913* @param context Matching Context Pointer914* @param peer Target Peer915* @param optlen Optional Data Length916* @param opt Optional Data917*/918void sendJoinRequest(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer, int optlen, const void * opt)919{920// Send Join Message921sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_JOIN, optlen, opt);922}923924/**925* Send Cancel Message to Peer (has various effects)926* @param context Matching Context Pointer927* @param peer Target Peer928* @param optlen Optional Data Length929* @param opt Optional Data930*/931void sendCancelMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer, int optlen, const void * opt)932{933// Send Cancel Message934sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_CANCEL, optlen, opt);935}936937/**938* Send Bulk Data to Peer939* @param context Matching Context Pointer940* @param peer Target Peer941* @param datalen Data Length942* @param data Data943*/944void sendBulkData(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer, int datalen, const void * data)945{946// Send Bulk Data Message947sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_BULK, datalen, data);948}949950/**951* Abort Bulk Data Transfer (if in progress)952* @param context Matching Context Pointer953* @param peer Target Peer954*/955void abortBulkTransfer(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer)956{957// Send Bulk Data Abort Message958sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_BULK_ABORT, 0, NULL);959}960961/**962* Notify all established Peers about new Kid in the Neighborhood963* @param context Matching Context Pointer964* @param peer New Kid965*/966void sendBirthMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer)967{968// Send Birth Message969sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_BIRTH, 0, NULL);970}971972/**973* Notify all established Peers about abandoned Child974* @param context Matching Context Pointer975* @param peer Abandoned Child976*/977void sendDeathMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberInternal * peer)978{979// Send Death Message980sendGenericMessage(context, PSP_ADHOC_MATCHING_INPUT_STACK, &peer->mac, PSP_ADHOC_MATCHING_PACKET_DEATH, 0, NULL);981}982983/**984* Return Number of Connected Peers985* @param context Matching Context Pointer986* @return Number of Connected Peers987*/988uint32_t countConnectedPeers(SceNetAdhocMatchingContext * context, const bool excludeTimedout)989{990// Peer Count991uint32_t count = 0;992993// Parent Mode994if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT)995{996// Number of Children + 1 Parent (Self)997count = countChildren(context, excludeTimedout) + 1;998}9991000// Child Mode1001else if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD)1002{1003// Default to 1 Child (Self)1004count = 1;10051006// Connected to Parent1007if (findParent(context) != NULL)1008{1009// Add Number of Siblings + 1 Parent1010count += 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 here1011}1012}10131014// P2P Mode1015else1016{1017// Default to 1 P2P Client (Self)1018count = 1;10191020// Connected to another P2P Client1021if (findP2P(context, excludeTimedout) != NULL)1022{1023// Add P2P Brother1024count++;1025}1026}10271028// Return Peer Count1029return count;1030}10311032/**1033* Spawn Local Event for Event Thread1034* @param context Matching Context Pointer1035* @param event Event ID1036* @param mac Event Source MAC1037* @param optlen Optional Data Length1038* @param opt Optional Data1039*/1040void spawnLocalEvent(SceNetAdhocMatchingContext * context, int event, SceNetEtherAddr * mac, int optlen, void * opt)1041{1042// Spawn Local Event1043sendGenericMessage(context, PSP_ADHOC_MATCHING_EVENT_STACK, mac, event, optlen, opt);1044}10451046/**1047* Handle Timeouts in Matching Context1048* @param context Matching Context Pointer1049*/1050void handleTimeout(SceNetAdhocMatchingContext * context)1051{1052peerlock.lock();1053// Iterate Peer List1054SceNetAdhocMatchingMemberInternal * peer = context->peerlist;1055while (peer != NULL && contexts != NULL && coreState != CORE_POWERDOWN)1056{1057// Get Next Pointer (to avoid crash on memory freeing)1058SceNetAdhocMatchingMemberInternal * next = peer->next;10591060u64_le now = CoreTiming::GetGlobalTimeUsScaled(); //time_now_d()*1000000.01061// 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 :(1062if (peer->state != 0 && static_cast<s64>(now - peer->lastping) > static_cast<s64>(context->timeout))1063{1064// Spawn Timeout Event. FIXME: Should we allow TIMEOUT Event to intervene joining process of Parent-Child too just like P2P Mode? (ie. Crazy Taxi uses P2P Mode)1065if ((context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && peer->state == PSP_ADHOC_MATCHING_PEER_PARENT) ||1066(context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) ||1067(context->mode == PSP_ADHOC_MATCHING_MODE_P2P &&1068(peer->state == PSP_ADHOC_MATCHING_PEER_P2P || peer->state == PSP_ADHOC_MATCHING_PEER_OFFER || peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST || peer->state == PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST || peer->state == PSP_ADHOC_MATCHING_PEER_CANCEL_IN_PROGRESS)))1069{1070// FIXME: TIMEOUT event should only be triggered on Parent/P2P mode and for Parent/P2P peer?1071spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_TIMEOUT, &peer->mac, 0, NULL);10721073INFO_LOG(Log::sceNet, "TimedOut Member Peer %s (%lld - %lld = %lld > %lld us)", mac2str(&peer->mac).c_str(), now, peer->lastping, (now - peer->lastping), context->timeout);10741075if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT)1076sendDeathMessage(context, peer);1077else1078sendCancelMessage(context, peer, 0, NULL);1079}1080}10811082// Move Pointer1083peer = next;1084}1085peerlock.unlock();1086}10871088/**1089* Recursive Stack Cleaner1090* @param node Current Thread Message Node1091*/1092void clearStackRecursive(ThreadMessage *& node)1093{1094// Not End of List1095if (node != NULL) clearStackRecursive(node->next);10961097// Free Last Existing Node of List (NULL is handled in _free)1098free(node);1099node = NULL;1100}11011102/**1103* Clear Thread Stack1104* @param context Matching Context Pointer1105* @param stack ADHOC_MATCHING_EVENT_STACK or ADHOC_MATCHING_INPUT_STACK1106*/1107void clearStack(SceNetAdhocMatchingContext * context, int stack)1108{1109if (context == NULL) return;11101111// Clear Event Stack1112if (stack == PSP_ADHOC_MATCHING_EVENT_STACK)1113{1114context->eventlock->lock();1115// Free Memory Recursively1116clearStackRecursive(context->event_stack);11171118// Destroy Reference1119context->event_stack = NULL;11201121context->eventlock->unlock();1122}11231124// Clear IO Stack1125else1126{1127context->inputlock->lock();1128// Free Memory Recursively1129clearStackRecursive(context->input_stack);11301131// Destroy Reference1132context->input_stack = NULL;11331134context->inputlock->unlock();1135}1136}11371138/**1139* Clear Peer List1140* @param context Matching Context Pointer1141*/1142void clearPeerList(SceNetAdhocMatchingContext * context)1143{1144// Acquire Peer Lock1145peerlock.lock();11461147// Iterate Peer List1148SceNetAdhocMatchingMemberInternal * peer = context->peerlist;1149while (peer != NULL)1150{1151// Grab Next Pointer1152context->peerlist = peer->next; //SceNetAdhocMatchingMemberInternal * next = peer->next;11531154// Delete Peer1155free(peer); //deletePeer(context, peer);1156// 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 peer1157//peer->lastping = CoreTiming::GetGlobalTimeUsScaled();11581159// Move Pointer1160peer = context->peerlist; //peer = next;1161}11621163// Free Peer Lock1164peerlock.unlock();1165}11661167void AfterMatchingMipsCall::DoState(PointerWrap & p) {1168auto s = p.Section("AfterMatchingMipsCall", 1, 4);1169if (!s)1170return;1171if (s >= 1) {1172Do(p, EventID);1173} else {1174EventID = -1;1175}1176if (s >= 4) {1177Do(p, contextID);1178Do(p, bufAddr);1179} else {1180contextID = -1;1181bufAddr = 0;1182}1183}11841185// 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)1186void AfterMatchingMipsCall::run(MipsCall &call) {1187if (context == NULL) {1188peerlock.lock();1189context = findMatchingContext(contextID);1190peerlock.unlock();1191}1192u32 v0 = currentMIPS->r[MIPS_REG_V0];1193if (__IsInInterrupt()) ERROR_LOG(Log::sceNet, "AfterMatchingMipsCall::run [ID=%i][Event=%d] is Returning Inside an Interrupt!", contextID, EventID);1194//SetMatchingInCallback(context, false);1195DEBUG_LOG(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);1196if (Memory::IsValidAddress(bufAddr)) userMemory.Free(bufAddr);1197//call.setReturnValue(v0);1198}11991200void AfterMatchingMipsCall::SetData(int ContextID, int eventId, u32_le BufAddr) {1201contextID = ContextID;1202EventID = eventId;1203bufAddr = BufAddr;1204peerlock.lock();1205context = findMatchingContext(ContextID);1206peerlock.unlock();1207}12081209bool SetMatchingInCallback(SceNetAdhocMatchingContext* context, bool IsInCB) {1210if (context == NULL) return false;1211peerlock.lock();1212context->IsMatchingInCB = IsInCB;1213peerlock.unlock();1214return IsInCB;1215}12161217bool IsMatchingInCallback(SceNetAdhocMatchingContext* context) {1218bool inCB = false;1219if (context == NULL) return inCB;1220peerlock.lock();1221inCB = (context->IsMatchingInCB);1222peerlock.unlock();1223return inCB;1224}12251226void AfterAdhocMipsCall::DoState(PointerWrap & p) {1227auto s = p.Section("AfterAdhocMipsCall", 1, 4);1228if (!s)1229return;1230if (s >= 3) {1231Do(p, HandlerID);1232Do(p, EventID);1233Do(p, argsAddr);1234} else {1235HandlerID = -1;1236EventID = -1;1237argsAddr = 0;1238}1239}12401241void AfterAdhocMipsCall::run(MipsCall& call) {1242u32 v0 = currentMIPS->r[MIPS_REG_V0];1243if (__IsInInterrupt()) ERROR_LOG(Log::sceNet, "AfterAdhocMipsCall::run [ID=%i][Event=%d] is Returning Inside an Interrupt!", HandlerID, EventID);1244SetAdhocctlInCallback(false);1245isAdhocctlBusy = false;1246DEBUG_LOG(Log::sceNet, "AfterAdhocMipsCall::run [ID=%i][Event=%d] [cbId: %u][retV0: %08x]", HandlerID, EventID, call.cbId, v0);1247//call.setReturnValue(v0);1248}12491250void AfterAdhocMipsCall::SetData(int handlerID, int eventId, u32_le ArgsAddr) {1251HandlerID = handlerID;1252EventID = eventId;1253argsAddr = ArgsAddr;1254}12551256int SetAdhocctlInCallback(bool IsInCB) {1257std::lock_guard<std::recursive_mutex> adhocGuard(adhocEvtMtx);1258IsAdhocctlInCB += (IsInCB?1:-1);1259return IsAdhocctlInCB;1260}12611262int IsAdhocctlInCallback() {1263std::lock_guard<std::recursive_mutex> adhocGuard(adhocEvtMtx);1264int inCB = IsAdhocctlInCB;1265return inCB;1266}12671268// Make sure MIPS calls have been fully executed before the next notifyAdhocctlHandlers1269void notifyAdhocctlHandlers(u32 flag, u32 error) {1270__UpdateAdhocctlHandlers(flag, error);1271}12721273// Matching callback is void function: typedef void(*SceNetAdhocMatchingHandler)(int id, int event, SceNetEtherAddr * peer, int optlen, void * opt);1274// 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 use1275// 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 thread1276void notifyMatchingHandler(SceNetAdhocMatchingContext * context, ThreadMessage * msg, void * opt, u32_le &bufAddr, u32_le &bufLen, u32_le * args) {1277// Don't share buffer address space with other mipscall in the queue since mipscalls aren't immediately executed1278MatchingArgs argsNew = { 0 };1279u32_le dataBufLen = msg->optlen + 8; //max(bufLen, msg->optlen + 8);1280u32_le dataBufAddr = userMemory.Alloc(dataBufLen); // We will free this memory after returning from mipscall. FIXME: Are these buffers supposed to be taken/pre-allocated from the memory pool during sceNetAdhocMatchingInit?1281uint8_t *dataPtr = Memory::GetPointerWriteRange(dataBufAddr, dataBufLen);1282if (dataPtr) {1283memcpy(dataPtr, &msg->mac, sizeof(msg->mac));1284if (msg->optlen > 0)1285memcpy(dataPtr + 8, opt, msg->optlen);12861287argsNew.data[1] = msg->opcode;1288argsNew.data[2] = dataBufAddr;1289argsNew.data[3] = msg->optlen;1290argsNew.data[4] = dataBufAddr + 8; // OptData Addr1291}1292else {1293argsNew.data[1] = PSP_ADHOC_MATCHING_EVENT_ERROR; // not sure where to put the error code for EVENT_ERROR tho1294//argsNew.data[2] = dataBufAddr; // FIXME: Is the MAC address mandatory (ie. can't be null pointer) even for EVENT_ERROR? Where should we put this MAC data in the case we failed to allocate the memory? may be on the memory pool?1295}1296argsNew.data[0] = context->id;1297argsNew.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 later12981299// ScheduleEvent_Threadsafe_Immediate seems to get mixed up with interrupt (returning from mipscall inside an interrupt) and getting invalid address before returning from interrupt1300__UpdateMatchingHandler(argsNew);1301}13021303void freeFriendsRecursive(SceNetAdhocctlPeerInfo * node, int32_t* count) {1304// End of List1305if (node == NULL) return;13061307// Increase Recursion Depth1308freeFriendsRecursive(node->next, count);13091310// Free Memory1311free(node);1312node = NULL;1313if (count != NULL) (*count)++;1314}13151316void timeoutFriendsRecursive(SceNetAdhocctlPeerInfo * node, int32_t* count) {1317// End of List1318if (node == NULL) return;13191320// Increase Recursion Depth1321timeoutFriendsRecursive(node->next, count);13221323// Set last timestamp1324node->last_recv = 0;1325if (count != NULL) (*count)++;1326}13271328void sendChat(const std::string &chatString) {1329SceNetAdhocctlChatPacketC2S chat;1330chat.base.opcode = OPCODE_CHAT;1331//TODO check network inited, check send success or not, chatlog.pushback error on failed send, pushback error on not connected1332if (friendFinderRunning) {1333// Send Chat to Server1334if (!chatString.empty()) {1335//maximum char allowed is 64 character for compability with original server (pro.coldbird.net)1336std::string message = chatString.substr(0, 60); // 64 return chat variable corrupted is it out of memory?1337strcpy(chat.message, message.c_str());1338//Send Chat Messages1339if (IsSocketReady((int)metasocket, false, true) > 0) {1340int chatResult = (int)send((int)metasocket, (const char*)&chat, sizeof(chat), MSG_NOSIGNAL);1341NOTICE_LOG(Log::sceNet, "Send Chat %s to Adhoc Server", chat.message);1342std::string name = g_Config.sNickName;13431344std::lock_guard<std::mutex> guard(chatLogLock);1345chatLog.emplace_back(name.substr(0, 8) + ": " + chat.message);1346chatMessageGeneration++;1347}1348}1349} else {1350std::lock_guard<std::mutex> guard(chatLogLock);1351auto n = GetI18NCategory(I18NCat::NETWORKING);1352chatLog.push_back(std::string(n->T("You're in Offline Mode, go to lobby or online hall")));1353chatMessageGeneration++;1354}1355}13561357std::vector<std::string> getChatLog() {1358std::lock_guard<std::mutex> guard(chatLogLock);1359// If the log gets large, trim it down.1360if (chatLog.size() > 50) {1361chatLog.erase(chatLog.begin(), chatLog.begin() + (chatLog.size() - 50));1362}1363return chatLog;1364}13651366int GetChatChangeID() {1367return chatMessageGeneration;1368}13691370int GetChatMessageCount() {1371return chatMessageCount;1372}13731374// 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 HLEs1375int friendFinder(){1376SetCurrentThreadName("FriendFinder");1377auto n = GetI18NCategory(I18NCat::NETWORKING);1378// Receive Buffer1379int rxpos = 0;1380uint8_t rx[1024];13811382// Chat Packet1383SceNetAdhocctlChatPacketC2S chat;1384chat.base.opcode = OPCODE_CHAT;13851386// Last Ping Time1387uint64_t lastping = 0;13881389// Last Time Reception got updated1390uint64_t lastreceptionupdate = 0;13911392uint64_t now;13931394// Log Startup1395INFO_LOG(Log::sceNet, "FriendFinder: Begin of Friend Finder Thread");13961397// Resolve and cache AdhocServer DNS1398addrinfo* resolved = nullptr;1399std::string err;1400g_adhocServerIP.in.sin_addr.s_addr = INADDR_NONE;1401if (g_Config.bEnableWlan && !net::DNSResolve(g_Config.proAdhocServer, "", &resolved, err)) {1402ERROR_LOG(Log::sceNet, "DNS Error Resolving %s\n", g_Config.proAdhocServer.c_str());1403g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("DNS Error Resolving ")) + g_Config.proAdhocServer);1404}1405if (resolved) {1406for (auto ptr = resolved; ptr != NULL; ptr = ptr->ai_next) {1407switch (ptr->ai_family) {1408case AF_INET:1409g_adhocServerIP.in = *(sockaddr_in*)ptr->ai_addr;1410break;1411}1412}1413net::DNSResolveFree(resolved);1414}1415g_adhocServerIP.in.sin_port = htons(SERVER_PORT);14161417// Finder Loop1418friendFinderRunning = true;1419while (friendFinderRunning) {1420// Acquire Network Lock1421//_acquireNetworkLock();14221423// Reconnect when disconnected while Adhocctl is still inited1424if (metasocket == (int)INVALID_SOCKET && netAdhocctlInited && isAdhocctlNeedLogin) {1425if (g_Config.bEnableWlan) {1426if (initNetwork(&product_code) == 0) {1427networkInited = true;1428INFO_LOG(Log::sceNet, "FriendFinder: Network [RE]Initialized");1429// At this point we are most-likely not in a Group within the Adhoc Server, so we should probably reset AdhocctlState1430adhocctlState = ADHOCCTL_STATE_DISCONNECTED;1431netAdhocGameModeEntered = false;1432isAdhocctlBusy = false;1433}1434else {1435networkInited = false;1436shutdown((int)metasocket, SD_BOTH);1437closesocket((int)metasocket);1438metasocket = (int)INVALID_SOCKET;1439}1440}1441}1442// Prevent retrying to Login again unless it was on demand1443isAdhocctlNeedLogin = false;14441445if (networkInited) {1446// Ping Server1447now = 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 stable1448// original code : ((sceKernelGetSystemTimeWide() - lastping) >= ADHOCCTL_PING_TIMEOUT)1449if (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 internet1450// Prepare Packet1451uint8_t opcode = OPCODE_PING;14521453// 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)1454if (IsSocketReady((int)metasocket, false, true) > 0) {1455int iResult = (int)send((int)metasocket, (const char*)&opcode, 1, MSG_NOSIGNAL);1456int error = errno;1457// KHBBS seems to be getting error 10053 often1458if (iResult == SOCKET_ERROR) {1459ERROR_LOG(Log::sceNet, "FriendFinder: Socket Error (%i) when sending OPCODE_PING", error);1460if (error != EAGAIN && error != EWOULDBLOCK) {1461networkInited = false;1462shutdown((int)metasocket, SD_BOTH);1463closesocket((int)metasocket);1464metasocket = (int)INVALID_SOCKET;1465g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Disconnected from AdhocServer")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(error) + ")");1466// Mark all friends as timedout since we won't be able to detects disconnected friends anymore without being connected to Adhoc Server1467peerlock.lock();1468timeoutFriendsRecursive(friends);1469peerlock.unlock();1470}1471}1472else {1473// Update Ping Time1474lastping = now;1475VERBOSE_LOG(Log::sceNet, "FriendFinder: Sending OPCODE_PING (%llu)", static_cast<unsigned long long>(now));1476}1477}1478}14791480// Check for Incoming Data1481if (IsSocketReady((int)metasocket, true, false) > 0) {1482int received = (int)recv((int)metasocket, (char*)(rx + rxpos), sizeof(rx) - rxpos, MSG_NOSIGNAL);14831484// Free Network Lock1485//_freeNetworkLock();14861487// Received Data1488if (received > 0) {1489// Fix Position1490rxpos += received;14911492// Log Incoming Traffic1493//printf("Received %d Bytes of Data from Server\n", received);1494INFO_LOG(Log::sceNet, "Received %d Bytes of Data from Adhoc Server", received);1495}1496}14971498// Calculate EnterGameMode Timeout to prevent waiting forever for disconnected players1499if (isAdhocctlBusy && adhocctlState == ADHOCCTL_STATE_DISCONNECTED && adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE && netAdhocGameModeEntered && static_cast<s64>(now - adhocctlStartTime) > netAdhocEnterGameModeTimeout) {1500netAdhocGameModeEntered = false;1501notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, ERROR_NET_ADHOC_TIMEOUT);1502}15031504// Handle Packets1505if (rxpos > 0) {1506// BSSID Packet1507if (rx[0] == OPCODE_CONNECT_BSSID) {1508// Enough Data available1509if (rxpos >= (int)sizeof(SceNetAdhocctlConnectBSSIDPacketS2C)) {1510// Cast Packet1511SceNetAdhocctlConnectBSSIDPacketS2C* packet = (SceNetAdhocctlConnectBSSIDPacketS2C*)rx;15121513INFO_LOG(Log::sceNet, "FriendFinder: Incoming OPCODE_CONNECT_BSSID [%s]", mac2str(&packet->mac).c_str());1514// Update User BSSID1515parameter.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 address15161517// 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?)1518// Notify Event Handlers1519if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {1520SceNetEtherAddr localMac;1521getLocalMac(&localMac);1522if (std::find_if(gameModeMacs.begin(), gameModeMacs.end(),1523[localMac](SceNetEtherAddr const& e) {1524return isMacMatch(&e, &localMac);1525}) == gameModeMacs.end()) {1526// 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 back1527gameModeMacs.push_back(localMac);15281529// 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 notified1530if (netAdhocGameModeEntered && gameModeMacs.size() >= requiredGameModeMacs.size()) {1531notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);1532}1533}1534else1535WARN_LOG(Log::sceNet, "GameMode SelfMember [%s] Already Existed!", mac2str(&localMac).c_str());1536}1537else {1538//adhocctlState = ADHOCCTL_STATE_CONNECTED;1539notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0);1540}15411542// Give time a little time1543//sceKernelDelayThread(adhocEventDelayMS * 1000);1544//sleep_ms(adhocEventDelayMS);15451546// Move RX Buffer1547memmove(rx, rx + sizeof(SceNetAdhocctlConnectBSSIDPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlConnectBSSIDPacketS2C));15481549// Fix RX Buffer Length1550rxpos -= sizeof(SceNetAdhocctlConnectBSSIDPacketS2C);1551}1552}15531554// Chat Packet1555else if (rx[0] == OPCODE_CHAT) {1556// Enough Data available1557if (rxpos >= (int)sizeof(SceNetAdhocctlChatPacketS2C)) {1558// Cast Packet1559SceNetAdhocctlChatPacketS2C* packet = (SceNetAdhocctlChatPacketS2C*)rx;1560INFO_LOG(Log::sceNet, "FriendFinder: Incoming OPCODE_CHAT");15611562// Fix strings with null-terminated1563packet->name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;1564packet->base.message[ADHOCCTL_MESSAGE_LEN - 1] = 0;15651566// Add Incoming Chat to HUD1567NOTICE_LOG(Log::sceNet, "Received chat message %s", packet->base.message);1568std::string incoming = "";1569std::string name = (char*)packet->name.data;1570incoming.append(name.substr(0, 8));1571incoming.append(": ");1572incoming.append((char*)packet->base.message);15731574std::lock_guard<std::mutex> guard(chatLogLock);1575chatLog.push_back(incoming);1576chatMessageGeneration++;1577chatMessageCount++;15781579// Move RX Buffer1580memmove(rx, rx + sizeof(SceNetAdhocctlChatPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlChatPacketS2C));15811582// Fix RX Buffer Length1583rxpos -= sizeof(SceNetAdhocctlChatPacketS2C);1584}1585}15861587// Connect Packet1588else if (rx[0] == OPCODE_CONNECT) {1589// Enough Data available1590if (rxpos >= (int)sizeof(SceNetAdhocctlConnectPacketS2C)) {1591// Cast Packet1592SceNetAdhocctlConnectPacketS2C* packet = (SceNetAdhocctlConnectPacketS2C*)rx;15931594// Fix strings with null-terminated1595packet->name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;15961597// Log Incoming Peer1598u32_le ipaddr = packet->ip;1599INFO_LOG(Log::sceNet, "FriendFinder: Incoming OPCODE_CONNECT [%s][%s][%s]", mac2str(&packet->mac).c_str(), ip2str(*(in_addr*)&ipaddr).c_str(), packet->name.data);16001601// Add User1602addFriend(packet);16031604// Make sure GameMode participants are all joined (including self MAC)1605if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {1606if (std::find_if(gameModeMacs.begin(), gameModeMacs.end(),1607[packet](SceNetEtherAddr const& e) {1608return isMacMatch(&e, &packet->mac);1609}) == gameModeMacs.end()) {1610// Arrange the order to be consistent on all players (Host on top), Existing players are sent in reverse by AdhocServer1611SceNetEtherAddr localMac;1612getLocalMac(&localMac);1613auto it = std::find_if(gameModeMacs.begin(), gameModeMacs.end(),1614[localMac](SceNetEtherAddr const& e) {1615return isMacMatch(&e, &localMac);1616});1617// Starting from our self the rest of new players will be added to the back1618if (it != gameModeMacs.end()) {1619gameModeMacs.push_back(packet->mac);1620}1621else {1622it = gameModeMacs.begin() + 1;1623gameModeMacs.insert(it, packet->mac);1624}16251626// From JPCSP: Join complete when all the required MACs have joined1627if (netAdhocGameModeEntered && requiredGameModeMacs.size() > 0 && gameModeMacs.size() == requiredGameModeMacs.size()) {1628// 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!1629//gameModeMacs = requiredGameModeMacs;1630notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);1631}1632}1633else1634WARN_LOG(Log::sceNet, "GameMode Member [%s] Already Existed!", mac2str(&packet->mac).c_str());1635}16361637// Update HUD User Count1638std::string name = (char*)packet->name.data;1639std::string incoming = "";1640incoming.append(name.substr(0, 8));1641incoming.append(" Joined ");1642//do we need ip?1643//joined.append((char *)packet->ip);16441645std::lock_guard<std::mutex> guard(chatLogLock);1646chatLog.push_back(incoming);1647chatMessageGeneration++;16481649#ifdef LOCALHOST_AS_PEER1650setUserCount(getActivePeerCount());1651#else1652// setUserCount(getActivePeerCount()+1);1653#endif16541655// Move RX Buffer1656memmove(rx, rx + sizeof(SceNetAdhocctlConnectPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlConnectPacketS2C));16571658// Fix RX Buffer Length1659rxpos -= sizeof(SceNetAdhocctlConnectPacketS2C);1660}1661}16621663// Disconnect Packet1664else if (rx[0] == OPCODE_DISCONNECT) {1665// Enough Data available1666if (rxpos >= (int)sizeof(SceNetAdhocctlDisconnectPacketS2C)) {1667// Cast Packet1668SceNetAdhocctlDisconnectPacketS2C* packet = (SceNetAdhocctlDisconnectPacketS2C*)rx;16691670DEBUG_LOG(Log::sceNet, "FriendFinder: OPCODE_DISCONNECT");16711672// Log Incoming Peer Delete Request1673INFO_LOG(Log::sceNet, "FriendFinder: Incoming Peer Data Delete Request...");16741675if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {1676auto peer = findFriendByIP(packet->ip);1677for (auto& gma : replicaGameModeAreas)1678if (isMacMatch(&gma.mac, &peer->mac_addr)) {1679gma.updateTimestamp = 0;1680break;1681}1682}16831684// Delete User by IP, should delete by MAC since IP can be shared (behind NAT) isn't?1685deleteFriendByIP(packet->ip);16861687// Update HUD User Count1688#ifdef LOCALHOST_AS_PEER1689setUserCount(_getActivePeerCount());1690#else1691//setUserCount(_getActivePeerCount()+1);1692#endif16931694// Move RX Buffer1695memmove(rx, rx + sizeof(SceNetAdhocctlDisconnectPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlDisconnectPacketS2C));16961697// Fix RX Buffer Length1698rxpos -= sizeof(SceNetAdhocctlDisconnectPacketS2C);1699}1700}17011702// Scan Packet1703else if (rx[0] == OPCODE_SCAN) {1704// Enough Data available1705if (rxpos >= (int)sizeof(SceNetAdhocctlScanPacketS2C)) {1706// Cast Packet1707SceNetAdhocctlScanPacketS2C* packet = (SceNetAdhocctlScanPacketS2C*)rx;17081709DEBUG_LOG(Log::sceNet, "FriendFinder: OPCODE_SCAN");17101711// Log Incoming Network Information1712INFO_LOG(Log::sceNet, "Incoming Group Information...");17131714// Multithreading Lock1715peerlock.lock();17161717// Allocate Structure Data1718SceNetAdhocctlScanInfo* group = (SceNetAdhocctlScanInfo*)malloc(sizeof(SceNetAdhocctlScanInfo));17191720// Allocated Structure Data1721if (group != NULL) {1722// Clear Memory, should this be done only when allocating new group?1723memset(group, 0, sizeof(SceNetAdhocctlScanInfo));17241725// Link to existing Groups1726group->next = newnetworks;17271728// Copy Group Name1729group->group_name = packet->group;17301731// Set Group Host1732group->bssid.mac_addr = packet->mac;17331734// Set group parameters1735// 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?)1736group->channel = parameter.channel; //(parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) ? defaultWlanChannel : parameter.channel;1737// 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.1738group->mode = std::max(ADHOCCTL_MODE_NORMAL, adhocctlCurrentMode); // default to ADHOCCTL_MODE_NORMAL17391740// Link into Group List1741newnetworks = group;1742}17431744// Multithreading Unlock1745peerlock.unlock();17461747// Move RX Buffer1748memmove(rx, rx + sizeof(SceNetAdhocctlScanPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlScanPacketS2C));17491750// Fix RX Buffer Length1751rxpos -= sizeof(SceNetAdhocctlScanPacketS2C);1752}1753}17541755// Scan Complete Packet1756else if (rx[0] == OPCODE_SCAN_COMPLETE) {1757DEBUG_LOG(Log::sceNet, "FriendFinder: OPCODE_SCAN_COMPLETE");1758// Log Scan Completion1759INFO_LOG(Log::sceNet, "FriendFinder: Incoming Scan complete response...");17601761// Reset current networks to prevent disbanded host to be listed again1762peerlock.lock();1763if (networks != newnetworks) {1764freeGroupsRecursive(networks);1765networks = newnetworks;1766}1767newnetworks = NULL;1768peerlock.unlock();17691770// Notify Event Handlers1771notifyAdhocctlHandlers(ADHOCCTL_EVENT_SCAN, 0);17721773// Move RX Buffer1774memmove(rx, rx + 1, sizeof(rx) - 1);17751776// Fix RX Buffer Length1777rxpos -= 1;1778}1779}1780}1781// This delay time should be 100ms when there is an event otherwise 500ms ?1782sleep_ms(10); // Using 1ms for faster response just like AdhocServer?17831784// Don't do anything if it's paused, otherwise the log will be flooded1785while (Core_IsStepping() && coreState != CORE_POWERDOWN && friendFinderRunning) sleep_ms(10);1786}17871788// Groups/Networks should be deallocated isn't?17891790// Prevent the games from having trouble to reInitiate Adhoc (the next NetInit -> PdpCreate after NetTerm)1791adhocctlState = ADHOCCTL_STATE_DISCONNECTED;1792friendFinderRunning = false;17931794// Log Shutdown1795INFO_LOG(Log::sceNet, "FriendFinder: End of Friend Finder Thread");17961797// Return Success1798return 0;1799}18001801int getActivePeerCount(const bool excludeTimedout) {1802// Counter1803int count = 0;18041805// #ifdef LOCALHOST_AS_PEER1806// // Increase for Localhost1807// count++;1808// #endif18091810// Peer Reference1811SceNetAdhocctlPeerInfo * peer = friends;18121813// Iterate Peers1814for (; peer != NULL; peer = peer->next) {1815// Increase Counter, Should we exclude peers pending for timed out?1816if (!excludeTimedout || peer->last_recv != 0)1817count++;1818}18191820// Return Result1821return count;1822}18231824int getLocalIp(sockaddr_in* SocketAddress) {1825if (isLocalServer) {1826SocketAddress->sin_addr = g_localhostIP.in.sin_addr;1827return 0;1828}18291830#if !PPSSPP_PLATFORM(SWITCH)1831if (metasocket != (int)INVALID_SOCKET) {1832struct sockaddr_in localAddr {};1833localAddr.sin_addr.s_addr = INADDR_ANY;1834socklen_t addrLen = sizeof(localAddr);1835int ret = getsockname((int)metasocket, (struct sockaddr*)&localAddr, &addrLen);1836// Note: Sometimes metasocket still contains a valid socket fd right after failed to connect to AdhocServer on a different thread, thus ended with 0.0.0.0 here1837if (SOCKET_ERROR != ret && localAddr.sin_addr.s_addr != 0) {1838SocketAddress->sin_addr = localAddr.sin_addr;1839return 0;1840}1841}1842#endif // !PPSSPP_PLATFORM(SWITCH)18431844// Fallback if not connected to AdhocServer1845// getifaddrs first appeared in glibc 2.3, On Android officially supported since __ANDROID_API__ >= 241846#if defined(_IFADDRS_H_) || (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3) || (__ANDROID_API__ >= 24)1847struct ifaddrs* ifAddrStruct = NULL;1848struct ifaddrs* ifa = NULL;18491850getifaddrs(&ifAddrStruct);1851if (ifAddrStruct != NULL) {1852for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {1853if (!ifa->ifa_addr) {1854continue;1855}1856if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP41857// is a valid IP4 Address1858SocketAddress->sin_addr = ((struct sockaddr_in*)ifa->ifa_addr)->sin_addr;1859break;1860}1861}1862freeifaddrs(ifAddrStruct);1863return 0;1864}18651866#else // Alternative way1867int sock = socket(AF_INET, SOCK_DGRAM, 0);1868if (sock != SOCKET_ERROR) {1869const 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 reachable1870uint16_t kDnsPort = 53;1871struct sockaddr_in serv {};1872u32 ipv4 = INADDR_NONE; // inet_addr(kGoogleDnsIp); // deprecated?1873inet_pton(AF_INET, kGoogleDnsIp, &ipv4);1874serv.sin_family = AF_INET;1875serv.sin_addr.s_addr = ipv4;1876serv.sin_port = htons(kDnsPort);18771878int err = connect(sock, (struct sockaddr*)&serv, sizeof(serv)); // connect should succeed even with SOCK_DGRAM1879if (err != SOCKET_ERROR) {1880struct sockaddr_in name {};1881socklen_t namelen = sizeof(name);1882err = getsockname(sock, (struct sockaddr*)&name, &namelen);1883if (err != SOCKET_ERROR) {1884SocketAddress->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 IP1885closesocket(sock);1886return 0;1887}1888}1889closesocket(sock);1890}1891#endif1892return -1;1893}18941895uint32_t getLocalIp(int sock) {1896struct sockaddr_in localAddr {};1897localAddr.sin_addr.s_addr = INADDR_ANY;1898socklen_t addrLen = sizeof(localAddr);1899getsockname(sock, (struct sockaddr*)&localAddr, &addrLen);1900if (isLocalServer) {1901localAddr.sin_addr = g_localhostIP.in.sin_addr;1902}1903return localAddr.sin_addr.s_addr;1904}19051906static std::vector<std::pair<uint32_t, uint32_t>> InitPrivateIPRanges() {1907struct sockaddr_in saNet {}, saMask{};1908std::vector<std::pair<uint32_t, uint32_t>> ip_ranges;19091910if (1 == inet_pton(AF_INET, "192.168.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.255.0.0", &(saMask.sin_addr)))1911ip_ranges.push_back({saNet.sin_addr.s_addr, saMask.sin_addr.s_addr});1912if (1 == inet_pton(AF_INET, "172.16.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.240.0.0", &(saMask.sin_addr)))1913ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });1914if (1 == inet_pton(AF_INET, "10.0.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.0.0.0", &(saMask.sin_addr)))1915ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });1916if (1 == inet_pton(AF_INET, "127.0.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.0.0.0", &(saMask.sin_addr)))1917ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });1918if (1 == inet_pton(AF_INET, "169.254.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.255.0.0", &(saMask.sin_addr)))1919ip_ranges.push_back({ saNet.sin_addr.s_addr, saMask.sin_addr.s_addr });19201921return ip_ranges;1922}19231924bool isPrivateIP(uint32_t ip) {1925static const std::vector<std::pair<uint32_t, uint32_t>> ip_ranges = InitPrivateIPRanges();1926for (auto& ipRange : ip_ranges) {1927if ((ip & ipRange.second) == (ipRange.first & ipRange.second)) // We can just use ipRange.first directly if it's already correctly formatted1928return true;1929}1930return false;1931}19321933bool isLoopbackIP(uint32_t ip) {1934return ((uint8_t*)&ip)[0] == 0x7f;1935}19361937void getLocalMac(SceNetEtherAddr * addr){1938// Read MAC Address from config1939uint8_t mac[ETHER_ADDR_LEN] = {0};1940if (PPSSPP_ID > 1) {1941memset(&mac, PPSSPP_ID, sizeof(mac));1942// Making sure the 1st 2-bits on the 1st byte of OUI are zero to prevent issue with some games (ie. Gran Turismo)1943mac[0] &= 0xfc;1944}1945else1946if (!ParseMacAddress(g_Config.sMACAddress, mac)) {1947ERROR_LOG(Log::sceNet, "Error parsing mac address %s", g_Config.sMACAddress.c_str());1948memset(&mac, 0, sizeof(mac));1949}1950memcpy(addr, mac, ETHER_ADDR_LEN);1951}19521953uint16_t getLocalPort(int sock) {1954struct sockaddr_in localAddr {};1955localAddr.sin_port = 0;1956socklen_t addrLen = sizeof(localAddr);1957getsockname(sock, (struct sockaddr*)&localAddr, &addrLen);1958return ntohs(localAddr.sin_port);1959}19601961u_long getAvailToRecv(int sock, int udpBufferSize) {1962u_long n = 0; // Typical MTU size is 15001963int err = -1;1964// Note: FIONREAD may have different behavior depends on the platform, according to https://stackoverflow.com/questions/9278189/how-do-i-get-amount-of-queued-data-for-udp-socket/9296481#92964811965#if defined(_WIN32)1966err = ioctlsocket(sock, FIONREAD, &n);1967#else1968err = ioctl(sock, FIONREAD, &n);1969#endif1970if (err < 0)1971return 0;19721973if (udpBufferSize > 0 && n > 0) {1974// TODO: May need to filter out packets from an IP that can't be translated to MAC address1975// TODO: Cap number of bytes of full DGRAM message(s) up to buffer size, but may cause Warriors Orochi 2 to get FPS drops1976}1977return n;1978}19791980int getSockMaxSize(int udpsock) {1981int n = PSP_ADHOC_PDP_MTU; // Typical MTU size is 15001982#if defined(SO_MAX_MSG_SIZE) // May not be available on all platform1983socklen_t m = sizeof(n);1984getsockopt(udpsock, SOL_SOCKET, SO_MAX_MSG_SIZE, (char*)&n, &m);1985#endif1986return n;1987}19881989int getSockBufferSize(int sock, int opt) { // opt = SO_RCVBUF/SO_SNDBUF1990int n = PSP_ADHOC_PDP_MFS; // 16384;1991socklen_t m = sizeof(n);1992getsockopt(sock, SOL_SOCKET, opt, (char *)&n, &m); // in linux the value is twice of the value being set using setsockopt1993return (n/2);1994}19951996int setSockBufferSize(int sock, int opt, int size) { // opt = SO_RCVBUF/SO_SNDBUF1997int n = size; // 8192; //163841998return setsockopt(sock, SOL_SOCKET, opt, (char *)&n, sizeof(n));1999}20002001int setSockMSS(int sock, int size) {2002int mss = size; // 1460;2003return setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, (char*)&mss, sizeof(mss));2004}20052006int setSockTimeout(int sock, int opt, unsigned long timeout_usec) { // opt = SO_SNDTIMEO/SO_RCVTIMEO2007if (timeout_usec > 0 && timeout_usec < minSocketTimeoutUS) timeout_usec = minSocketTimeoutUS; // Override timeout for high latency multiplayer2008#if defined(_WIN32)2009unsigned long optval = timeout_usec / 1000UL;2010if (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)2011#elif defined(__APPLE__)2012struct timeval optval;2013optval.tv_sec = static_cast<long>(timeout_usec) / 1000000L;2014optval.tv_usec = static_cast<long>(timeout_usec) % 1000000L;2015#else2016struct timeval optval = { static_cast<long>(timeout_usec) / 1000000L, static_cast<long>(timeout_usec) % 1000000L };2017#endif2018return setsockopt(sock, SOL_SOCKET, opt, (char*)&optval, sizeof(optval));2019}20202021int getSockError(int sock) {2022int result = 0;2023socklen_t result_len = sizeof(result);2024if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&result, &result_len) < 0) {2025result = errno;2026}2027return result;2028}20292030int getSockNoDelay(int tcpsock) {2031int opt = 0;2032socklen_t optlen = sizeof(opt);2033getsockopt(tcpsock, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, &optlen);2034return opt;2035}20362037//#define TCP_QUICKACK 0x0c2038int setSockNoDelay(int tcpsock, int flag) {2039int opt = flag;2040// Disable ACK Delay when supported2041#if defined(TCP_QUICKACK)2042setsockopt(tcpsock, IPPROTO_TCP, TCP_QUICKACK, (char*)&opt, sizeof(opt));2043#elif defined(_WIN32)2044#if !defined(SIO_TCP_SET_ACK_FREQUENCY)2045#define SIO_TCP_SET_ACK_FREQUENCY _WSAIOW(IOC_VENDOR,23)2046#endif2047int freq = flag? 1:2; // can be 1..255, default is 2 (delayed 200ms)2048DWORD retbytes = 0;2049WSAIoctl(tcpsock, SIO_TCP_SET_ACK_FREQUENCY, &freq, sizeof(freq), NULL, 0, &retbytes, NULL, NULL);2050#endif2051// Disable Nagle Algo2052return setsockopt(tcpsock, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, sizeof(opt));2053}20542055int setSockNoSIGPIPE(int sock, int flag) {2056// Set SIGPIPE when supported (ie. BSD/MacOS X)2057int opt = flag;2058#if defined(SO_NOSIGPIPE)2059// Note: Linux might have SO_NOSIGPIPE defined too, but using it on setsockopt will result to EINVAL error2060return setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&opt, sizeof(opt));2061#endif2062return -1;2063}20642065int setSockReuseAddrPort(int sock) {2066int opt = 1;2067// Should we set SO_BROADCAST too for SO_REUSEADDR to works like SO_REUSEPORT ?2068// Set SO_REUSEPORT also when supported (ie. Android)2069#if defined(SO_REUSEPORT)2070setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&opt, sizeof(opt));2071#endif2072return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));2073}20742075int setUDPConnReset(int udpsock, bool enabled) {2076// On Windows: Connection Reset error on UDP could cause a strange behavior https://stackoverflow.com/questions/34242622/windows-udp-sockets-recvfrom-fails-with-error-100542077#if defined(_WIN32)2078#if !defined(SIO_UDP_CONNRESET)2079#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12)2080#endif2081BOOL bNewBehavior = enabled;2082DWORD dwBytesReturned = 0;2083return WSAIoctl(udpsock, SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL);2084#endif2085return -1;2086}20872088#if !defined(TCP_KEEPIDLE) && !PPSSPP_PLATFORM(SWITCH)2089#define TCP_KEEPIDLE TCP_KEEPALIVE //TCP_KEEPIDLE on Linux is equivalent to TCP_KEEPALIVE on macOS2090#endif2091// VS 2017 compatibility2092#if _MSC_VER2093#ifndef TCP_KEEPCNT2094#define TCP_KEEPCNT 162095#endif2096#ifndef TCP_KEEPINTVL2097#define TCP_KEEPINTVL 172098#endif2099#endif2100int setSockKeepAlive(int sock, bool keepalive, const int keepinvl, const int keepcnt, const int keepidle) {2101int optval = keepalive ? 1 : 0;2102int optlen = sizeof(optval);2103int result = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, optlen);2104#if !PPSSPP_PLATFORM(SWITCH) && !PPSSPP_PLATFORM(OPENBSD)2105if (result == 0 && keepalive) {2106if (getsockopt(sock, SOL_SOCKET, SO_TYPE, (char*)&optval, (socklen_t*)&optlen) == 0 && optval == SOCK_STREAM) {2107optlen = sizeof(optval);2108optval = keepidle; //180 sec2109setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&optval, optlen);2110optval = keepinvl; //60 sec2111setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&optval, optlen);2112optval = keepcnt; //202113setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (char*)&optval, optlen);2114}2115}2116#endif // !PPSSPP_PLATFORM(SWITCH) && !PPSSPP_PLATFORM(OPENBSD)2117return result;2118}21192120/**2121* Return the Number of Players with the chosen Nickname in the Local Users current Network2122* @param nickname To-be-searched Nickname2123* @return Number of matching Players2124*/2125int getNicknameCount(const char * nickname)2126{2127// Counter2128int count = 0;21292130// Local Nickname Matches2131if (strncmp((char *)¶meter.nickname.data, nickname, ADHOCCTL_NICKNAME_LEN) == 0) count++;21322133// Peer Reference2134SceNetAdhocctlPeerInfo * peer = friends;21352136// Iterate Peers2137for (; peer != NULL; peer = peer->next)2138{2139// Match found2140if (peer->last_recv != 0 && strncmp((char *)&peer->nickname.data, nickname, ADHOCCTL_NICKNAME_LEN) == 0) count++;2141}21422143// Return Result2144return count;2145}21462147/**2148* PDP Socket Counter2149* @return Number of internal PDP Sockets2150*/2151int getPDPSocketCount()2152{2153// Socket Counter2154int counter = 0;21552156// Count Sockets2157for (int i = 0; i < MAX_SOCKET; i++)2158if (adhocSockets[i] != NULL && adhocSockets[i]->type == SOCK_PDP)2159counter++;21602161// Return Socket Count2162return counter;2163}21642165int getPTPSocketCount() {2166// Socket Counter2167int counter = 0;21682169// Count Sockets2170for (int i = 0; i < MAX_SOCKET; i++)2171if (adhocSockets[i] != NULL && adhocSockets[i]->type == SOCK_PTP)2172counter++;21732174// Return Socket Count2175return counter;2176}21772178int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){2179auto n = GetI18NCategory(I18NCat::NETWORKING);2180int iResult = 0;2181metasocket = (int)INVALID_SOCKET;2182metasocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);2183if (metasocket == INVALID_SOCKET){2184ERROR_LOG(Log::sceNet, "Invalid socket");2185return SOCKET_ERROR;2186}2187setSockKeepAlive((int)metasocket, true);2188// Disable Nagle Algo to prevent delaying small packets2189setSockNoDelay((int)metasocket, 1);2190// Switch to Nonblocking Behaviour2191changeBlockingMode((int)metasocket, 1);2192// Ignore SIGPIPE when supported (ie. BSD/MacOS)2193setSockNoSIGPIPE((int)metasocket, 1);21942195// 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 AdhocServer2196// (may not works in WinXP/2003 for IPv4 due to "Weak End System" model)2197if (isLoopbackIP(g_adhocServerIP.in.sin_addr.s_addr)) {2198int on = 1;2199// 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 :(2200setsockopt((int)metasocket, SOL_SOCKET, SO_DONTROUTE, (const char*)&on, sizeof(on));2201setSockReuseAddrPort((int)metasocket);22022203g_localhostIP.in.sin_port = 0;2204// Bind Local Address to Socket2205iResult = bind((int)metasocket, &g_localhostIP.addr, sizeof(g_localhostIP.addr));2206if (iResult == SOCKET_ERROR) {2207ERROR_LOG(Log::sceNet, "Bind to alternate localhost[%s] failed(%i).", ip2str(g_localhostIP.in.sin_addr).c_str(), iResult);2208g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to Bind Localhost IP")) + " " + ip2str(g_localhostIP.in.sin_addr).c_str());2209}2210}22112212// Default/Initial Network Parameters2213memset(¶meter, 0, sizeof(parameter));2214strncpy((char *)¶meter.nickname.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN);2215parameter.nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;2216parameter.channel = g_Config.iWlanAdhocChannel;2217// 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)2218if (parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) parameter.channel = defaultWlanChannel; // Faked Active channel to default channel2219//getLocalMac(¶meter.bssid.mac_addr);22202221// Default ProductId2222product_code.type = adhoc_id->type;2223memcpy(product_code.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN);22242225// Don't need to connect if AdhocServer DNS was not resolved2226if (g_adhocServerIP.in.sin_addr.s_addr == INADDR_NONE)2227return SOCKET_ERROR;22282229// Don't need to connect if AdhocServer IP is the same with this instance localhost IP and having AdhocServer disabled2230if (g_adhocServerIP.in.sin_addr.s_addr == g_localhostIP.in.sin_addr.s_addr && !g_Config.bEnableAdhocServer)2231return SOCKET_ERROR;22322233// Connect to Adhoc Server2234int errorcode = 0;2235int cnt = 0;2236DEBUG_LOG(Log::sceNet, "InitNetwork: Connecting to AdhocServer");2237iResult = connect((int)metasocket, &g_adhocServerIP.addr, sizeof(g_adhocServerIP));2238errorcode = errno;22392240if (iResult == SOCKET_ERROR && errorcode != EISCONN) {2241u64 startTime = (u64)(time_now_d() * 1000000.0);2242bool done = false;2243while (!done) {2244if (coreState == CORE_POWERDOWN)2245return iResult;22462247done = (IsSocketReady((int)metasocket, false, true) > 0);2248struct sockaddr_in sin;2249socklen_t sinlen = sizeof(sin);2250memset(&sin, 0, sinlen);2251// Ensure that the connection really established or not, since "select" alone can't accurately detects it2252done &= (getpeername((int)metasocket, (struct sockaddr*)&sin, &sinlen) != SOCKET_ERROR);2253u64 now = (u64)(time_now_d() * 1000000.0);2254if (static_cast<s64>(now - startTime) > adhocDefaultTimeout) {2255if (connectInProgress(errorcode))2256errorcode = ETIMEDOUT;2257break;2258}2259sleep_ms(10);2260}2261if (!done) {2262ERROR_LOG(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));2263g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to connect to Adhoc Server")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(errorcode) + ")");2264return iResult;2265}2266}22672268// Prepare Login Packet2269SceNetAdhocctlLoginPacketC2S packet;2270packet.base.opcode = OPCODE_LOGIN;2271SceNetEtherAddr addres;2272getLocalMac(&addres);2273packet.mac = addres;2274strncpy((char *)&packet.name.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN);2275packet.name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0;2276memcpy(packet.game.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN);22772278IsSocketReady((int)metasocket, false, true, nullptr, adhocDefaultTimeout);2279DEBUG_LOG(Log::sceNet, "InitNetwork: Sending LOGIN OPCODE %d", packet.base.opcode);2280int sent = (int)send((int)metasocket, (char*)&packet, sizeof(packet), MSG_NOSIGNAL);2281if (sent > 0) {2282socklen_t addrLen = sizeof(LocalIP);2283memset(&LocalIP, 0, addrLen);2284getsockname((int)metasocket, &LocalIP, &addrLen);2285g_OSD.Show(OSDType::MESSAGE_SUCCESS, n->T("Network initialized"), 1.0, "networkinit");2286return 0;2287} else {2288return SOCKET_ERROR;2289}2290}22912292bool isZeroMAC(const SceNetEtherAddr* addr) {2293return (memcmp(addr->data, "\x00\x00\x00\x00\x00\x00", ETHER_ADDR_LEN) == 0);2294}22952296bool isBroadcastMAC(const SceNetEtherAddr * addr) {2297return (memcmp(addr->data, "\xFF\xFF\xFF\xFF\xFF\xFF", ETHER_ADDR_LEN) == 0);2298}22992300bool resolveIP(uint32_t ip, SceNetEtherAddr * mac) {2301sockaddr_in addr;2302getLocalIp(&addr);2303uint32_t localIp = addr.sin_addr.s_addr;23042305if (ip == localIp || ip == g_localhostIP.in.sin_addr.s_addr) {2306getLocalMac(mac);2307return true;2308}23092310// Multithreading Lock2311std::lock_guard<std::recursive_mutex> peer_guard(peerlock);23122313// Peer Reference2314SceNetAdhocctlPeerInfo * peer = friends;23152316// Iterate Peers2317for (; peer != NULL; peer = peer->next) {2318// Found Matching Peer2319if (peer->ip_addr == ip) {2320// Copy Data2321*mac = peer->mac_addr;23222323// Return Success2324return true;2325}2326}23272328// Peer not found2329return false;2330}23312332bool resolveMAC(SceNetEtherAddr* mac, uint32_t* ip, u16* port_offset) {2333// Get Local MAC Address2334SceNetEtherAddr localMac;2335getLocalMac(&localMac);2336// Local MAC Requested2337if (isMacMatch(&localMac, mac)) {2338// Get Local IP Address2339sockaddr_in sockAddr;2340getLocalIp(&sockAddr);2341*ip = sockAddr.sin_addr.s_addr;2342if (port_offset)2343*port_offset = portOffset;2344return true; // return succes2345}23462347// Multithreading Lock2348std::lock_guard<std::recursive_mutex> peer_guard(peerlock);23492350// Peer Reference2351SceNetAdhocctlPeerInfo * peer = friends;23522353// Iterate Peers2354for (; peer != NULL; peer = peer->next) {2355// Found Matching Peer2356if (isMacMatch(&peer->mac_addr, mac)) {2357// Copy Data2358*ip = peer->ip_addr;2359if (port_offset)2360*port_offset = peer->port_offset;2361// Return Success2362return true;2363}2364}23652366// Peer not found2367return false;2368}23692370bool validNetworkName(const SceNetAdhocctlGroupName * group_name) {2371// Result2372bool valid = true;23732374// Name given2375if (group_name != NULL) {2376// Iterate Name Characters2377for (int i = 0; i < ADHOCCTL_GROUPNAME_LEN && valid; i++) {2378// End of Name2379if (group_name->data[i] == 0) break;23802381// Not a digit2382if (group_name->data[i] < '0' || group_name->data[i] > '9') {2383// Not 'A' to 'Z'2384if (group_name->data[i] < 'A' || group_name->data[i] > 'Z') {2385// Not 'a' to 'z'2386if (group_name->data[i] < 'a' || group_name->data[i] > 'z') {2387// Invalid Name2388valid = false;2389}2390}2391}2392}2393}2394// Return Result2395return valid;2396}23972398u64 join32(u32 num1, u32 num2){2399return (u64)num2 << 32 | num1;2400}24012402void split64(u64 num, int buff[]){2403int num1 = (int)(num&firstMask);2404int num2 = (int)((num&secondMask) >> 32);2405buff[0] = num1;2406buff[1] = num2;2407}24082409const char* getMatchingEventStr(int code) {2410const char *buf = NULL;2411switch (code) {2412case PSP_ADHOC_MATCHING_EVENT_HELLO:2413buf = "HELLO"; break;2414case PSP_ADHOC_MATCHING_EVENT_REQUEST:2415buf = "JOIN"; break;2416case PSP_ADHOC_MATCHING_EVENT_LEAVE:2417buf = "LEAVE"; break;2418case PSP_ADHOC_MATCHING_EVENT_DENY:2419buf = "REJECT"; break;2420case PSP_ADHOC_MATCHING_EVENT_CANCEL:2421buf = "CANCEL"; break;2422case PSP_ADHOC_MATCHING_EVENT_ACCEPT:2423buf = "ACCEPT"; break;2424case PSP_ADHOC_MATCHING_EVENT_ESTABLISHED:2425buf = "ESTABLISHED"; break;2426case PSP_ADHOC_MATCHING_EVENT_TIMEOUT:2427buf = "TIMEOUT"; break;2428case PSP_ADHOC_MATCHING_EVENT_ERROR:2429buf = "ERROR"; break;2430case PSP_ADHOC_MATCHING_EVENT_BYE:2431buf = "DISCONNECT"; break;2432case PSP_ADHOC_MATCHING_EVENT_DATA:2433buf = "DATA"; break;2434case PSP_ADHOC_MATCHING_EVENT_DATA_ACK:2435buf = "DATA_ACK"; break;2436case PSP_ADHOC_MATCHING_EVENT_DATA_TIMEOUT:2437buf = "DATA_TIMEOUT"; break;2438case PSP_ADHOC_MATCHING_EVENT_INTERNAL_PING:2439buf = "INTERNAL_PING"; break;2440default:2441buf = "UNKNOWN";2442}2443return buf;2444}24452446const char* getMatchingOpcodeStr(int code) {2447const char *buf = NULL;2448switch (code) {2449case PSP_ADHOC_MATCHING_PACKET_PING:2450buf = "PING"; break;2451case PSP_ADHOC_MATCHING_PACKET_HELLO:2452buf = "HELLO"; break;2453case PSP_ADHOC_MATCHING_PACKET_JOIN:2454buf = "JOIN"; break;2455case PSP_ADHOC_MATCHING_PACKET_ACCEPT:2456buf = "ACCEPT"; break;2457case PSP_ADHOC_MATCHING_PACKET_CANCEL:2458buf = "CANCEL"; break;2459case PSP_ADHOC_MATCHING_PACKET_BULK:2460buf = "BULK"; break;2461case PSP_ADHOC_MATCHING_PACKET_BULK_ABORT:2462buf = "BULK_ABORT"; break;2463case PSP_ADHOC_MATCHING_PACKET_BIRTH:2464buf = "BIRTH"; break;2465case PSP_ADHOC_MATCHING_PACKET_DEATH:2466buf = "DEATH"; break;2467case PSP_ADHOC_MATCHING_PACKET_BYE:2468buf = "BYE"; break;2469default:2470buf = "UNKNOWN";2471}2472return buf;2473}2474247524762477