From ee86058ec7b02c0c37a2ba1ddcf3f8e067f2c8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 28 Oct 2026 19:04:59 +0100 Subject: [PATCH] sceNetAdhocctl: Leave the console out of its own peer lookups; give groups their own BSSID From the basic and lifecycle pair tests, recorded on two PSPs: - GetNameByAddr for the console's own MAC gives NO_ENTRY, and GetAddrByName doesn't find the console itself. - GetPeerInfo with a size too small for the entry gives INVALID_BUFLEN. - A PTP connection accepted over the relay reports the peer's port as the game sees it, not the relay's offset port. - The built-in server makes up a locally administered BSSID for each group, which stays when the creator leaves. It was the MAC of the oldest member. Co-Authored-By: Claude Opus 5.5 --- Core/HLE/proAdhoc.cpp | 3 +-- Core/HLE/proAdhocServer.cpp | 27 +++++++++++++-------------- Core/HLE/proAdhocServer.h | 3 +++ Core/HLE/sceNetAdhoc.cpp | 37 +++++++++---------------------------- 4 files changed, 26 insertions(+), 44 deletions(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index b249b35d69..f94f599465 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -2258,8 +2258,7 @@ int getNicknameCount(const char * nickname) // Counter int count = 0; - // Local Nickname Matches - if (strncmp((char *)¶meter.nickname.data, nickname, ADHOCCTL_NICKNAME_LEN) == 0) count++; + // The console itself isn't in the peer list (pspautotests net/adhoc/pair/basic). // Peer Reference SceNetAdhocctlPeerInfo * peer = friends; diff --git a/Core/HLE/proAdhocServer.cpp b/Core/HLE/proAdhocServer.cpp index 2921dc793f..012bb2c0cc 100644 --- a/Core/HLE/proAdhocServer.cpp +++ b/Core/HLE/proAdhocServer.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -813,6 +814,15 @@ void connect_user(SceNetAdhocctlUserNode * user, SceNetAdhocctlGroupName * group // Copy Group Name g->group = *group; + // A PSP group's BSSID is a locally administered address of its own, not a member's MAC. It + // stays when the creator leaves, and a group created again gets a new one (pspautotests + // net/adhoc/pair/lifecycle). + static std::mt19937 bssidRng(std::random_device{}()); + for (int i = 0; i < ETHER_ADDR_LEN; i++) { + g->bssid.data[i] = (uint8_t)bssidRng(); + } + g->bssid.data[0] = (g->bssid.data[0] & 0xFC) | 0x02; + // Increase Group Counter for Game g->game->groupcount++; } @@ -860,13 +870,12 @@ void connect_user(SceNetAdhocctlUserNode * user, SceNetAdhocctlGroupName * group iResult = (int)send(user->stream, (const char*)&packet, sizeof(packet), MSG_NOSIGNAL); if (iResult < 0) ERROR_LOG(Log::sceNet, "AdhocServer: connect_user[send user] (Socket error %d)", socket_errno); - // Set BSSID - if(peer->group_next == NULL) bssid.mac = peer->resolver.mac; - // Move Pointer peer = peer->group_next; } + bssid.mac = g->bssid; + // Link User to Group user->group_next = g->player; if(g->player != NULL) g->player->group_prev = user; @@ -1057,17 +1066,7 @@ void send_scan_results(SceNetAdhocctlUserNode * user) // Set Group Name packet.group = group->group; - // Iterate Players in Network Group - SceNetAdhocctlUserNode * peer = group->player; - for(; peer != NULL; peer = peer->group_next) - { - // Found Network Founder - if(peer->group_next == NULL) - { - // Set Group Host MAC - packet.mac = peer->resolver.mac; - } - } + packet.mac = group->bssid; // Send Group Packet int iResult = (int)send(user->stream, (const char*)&packet, sizeof(packet), MSG_NOSIGNAL); diff --git a/Core/HLE/proAdhocServer.h b/Core/HLE/proAdhocServer.h index 63a2897652..df2ff77b0e 100644 --- a/Core/HLE/proAdhocServer.h +++ b/Core/HLE/proAdhocServer.h @@ -262,6 +262,9 @@ struct SceNetAdhocctlGroupNode { // Double-Linked Player List SceNetAdhocctlUserNode * player; + + // Made up when the group is created, and kept while it lasts + SceNetEtherAddr bssid; }; // User Count diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 77abd85e77..d766b00d8d 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -1462,7 +1462,7 @@ static int ptp_accept_postoffice(int idx, SceNetEtherAddr *saddr, uint16_t *spor internal->data.ptp.laddr = adhocSockets[idx]->data.ptp.laddr; internal->data.ptp.lport = adhocSockets[idx]->data.ptp.lport; internal->data.ptp.paddr = mac_cpy; - internal->data.ptp.pport = port_cpy; + internal->data.ptp.pport = reverse_port_simple(port_cpy); internal->data.ptp.state = ADHOC_PTP_STATE_ESTABLISHED; internal->data.ptp.rcv_sb_cc = adhocSockets[idx]->data.ptp.rcv_sb_cc; internal->data.ptp.snd_sb_cc = adhocSockets[idx]->data.ptp.snd_sb_cc; @@ -4055,16 +4055,11 @@ static int sceNetAdhocctlGetNameByAddr(MIPSState *mips, const char *mac, u32 nam SceNetEtherAddr localmac; getLocalMac(&localmac); - // Local MAC Matches + // The console itself isn't in the peer list: NO_ENTRY, and the name isn't written (pspautotests + // net/adhoc/pair/basic). if (isMacMatch(&localmac, (const SceNetEtherAddr*)mac)) { - // Write Data - *nickname = parameter.nickname; - - DEBUG_LOG(Log::sceNet, "sceNetAdhocctlGetNameByAddr - [PlayerName:%s]", (char*)nickname); - - // Return Success - return 0; + return hleLogDebug(Log::sceNet, SCE_NET_ADHOC_ERROR_NO_ENTRY, "local MAC"); } // Multithreading Lock @@ -4120,7 +4115,9 @@ int sceNetAdhocctlGetPeerInfo(MIPSState *mips, const char *mac, int size, u32 pe } // Library initialized if (netAdhocctlInited) { - if ((size < (int)sizeof(SceNetAdhocctlPeerInfoEmu)) || (buf == NULL)) return SCE_NET_ADHOCCTL_ERROR_INVALID_ARG; + if (buf == NULL) return SCE_NET_ADHOCCTL_ERROR_INVALID_ARG; + // A size too small for the entry gives the ad hoc library's INVALID_BUFLEN (pspautotests net/adhoc/pair/basic). + if (size < (int)sizeof(SceNetAdhocctlPeerInfoEmu)) return hleLogError(Log::sceNet, SCE_NET_ADHOC_ERROR_INVALID_BUFLEN, "buffer too small"); int retval = SCE_NET_ADHOC_ERROR_NO_ENTRY; // -1; @@ -6735,24 +6732,8 @@ static int sceNetAdhocctlGetAddrByName(MIPSState *mips, const char *nickName, u3 // Minimum Space available if (requestcount > 0) { - // Local Nickname Matches - if (strncmp((char *)¶meter.nickname.data, nickName, ADHOCCTL_NICKNAME_LEN) == 0) - { - // Get Local IP Address - sockaddr_in addr; - SceNetEtherAddr mac; - - getLocalIp(&addr); - buf[discovered].nickname = parameter.nickname; - buf[discovered].nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char - getLocalMac(&mac); - buf[discovered].mac_addr = mac; - buf[discovered].flags = 0x0400; - u64 lastrecv = std::max(0LL, static_cast(CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta)); - buf[discovered++].last_recv = lastrecv; - - DEBUG_LOG(Log::sceNet, "Peer [%s][%s][%s][%llu]", mac2str(&mac).c_str(), ip2str(addr.sin_addr).c_str(), nickName, lastrecv); - } + // The console itself isn't in the peer list, so it's never found here (pspautotests + // net/adhoc/pair/basic). // Peer Reference SceNetAdhocctlPeerInfo * peer = friends;