diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt
index ae05e867f9..fc3a55cb0a 100644
--- a/Core/CMakeLists.txt
+++ b/Core/CMakeLists.txt
@@ -501,6 +501,7 @@ add_library(Core STATIC
HLE/sceNetApctl.h
HLE/sceNetResolver.cpp
HLE/sceNetResolver.h
+ HLE/AdhocTransport.h
HLE/proAdhoc.h
HLE/proAdhoc.cpp
HLE/proAdhocServer.h
diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 30b7d563a2..ccb032b34e 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -949,6 +949,7 @@
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index a97d899e4f..ac4264fa14 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -1902,6 +1902,9 @@
Debugger
+
+ HLE\Libraries
+
HLE\Libraries
diff --git a/Core/HLE/AdhocTransport.h b/Core/HLE/AdhocTransport.h
new file mode 100644
index 0000000000..93067cf648
--- /dev/null
+++ b/Core/HLE/AdhocTransport.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#pragma once
+
+#include "Core/HLE/proAdhoc.h"
+#include "Core/HLE/sceNetAdhoc.h"
+
+// How ad hoc sockets reach the other consoles: through the aemu_postoffice relay, or directly over the
+// host's own UDP/TCP sockets (P2P). The sceNetAdhoc functions do everything a PSP does the same either
+// way, and call one of these for the parts that depend on the transport. Transport() picks the one in
+// use, which follows serverHasRelay.
+class AdhocTransport {
+public:
+ virtual ~AdhocTransport() {}
+
+ // PDP
+
+ // Opens the host side of a PDP socket and registers it in adhocSockets. Returns the socket ID, or an
+ // error. The arguments have been checked.
+ virtual int PdpCreate(const SceNetEtherAddr *saddr, int port, int bufferSize, u32 flag, bool isClient, bool enetReliable) = 0;
+ // Closes it and frees its slot.
+ virtual int PdpDelete(int id) = 0;
+ // Sends one datagram to one peer, whose address resolveMAC() has filled into target. Returns the
+ // number of bytes sent, or SOCKET_ERROR with an errno value in *error.
+ virtual int PdpSendTo(int id, const SceNetEtherAddr *mac, uint16_t dport, const sockaddr_in &target, const void *data, int len, int *error) = 0;
+ // The rest of sceNetAdhocPdpRecv once the arguments, the socket and its alerts have been checked.
+ virtual int PdpRecv(int id, SceNetEtherAddr *saddr, uint16_t *sport, void *buf, int *len, u32 timeout, int flag) = 0;
+ // The rest of a blocking receive's retry, after the socket and its alerts have been checked.
+ virtual int DoBlockingPdpRecv(AdhocSocketRequest &req, s64 &result) = 0;
+ // Updates rcv_sb_cc, the bytes waiting to be received, for sceNetAdhocGetPdpStat.
+ virtual void PdpUpdateRecvStat(int id, AdhocSocket *sock) = 0;
+ // Whether game mode should try sending to / receiving from its socket now.
+ virtual bool PdpReadyToSend(AdhocSocket *sock) = 0;
+ virtual bool PdpReadyToRecv(AdhocSocket *sock) = 0;
+};
+
+AdhocTransport &Transport();
diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp
index cddca16fdb..b0ed494a9f 100644
--- a/Core/HLE/sceNetAdhoc.cpp
+++ b/Core/HLE/sceNetAdhoc.cpp
@@ -57,6 +57,7 @@
#include "Core/HLE/sceKernelModule.h"
#include "Core/HLE/sceKernelInterrupt.h"
#include "Core/HLE/sceNetAdhoc.h"
+#include "Core/HLE/AdhocTransport.h"
#include "Core/HLE/sceNet.h"
#include "Core/HLE/proAdhocServer.h"
#include "Core/HLE/KernelWaitHelpers.h"
@@ -382,6 +383,43 @@ int RecreatePtpSocket(int ptpId);
int NetAdhocGameMode_DeleteMaster();
int NetAdhocctl_ExitGameMode();
int NetAdhocPtp_Connect(int id, int timeout, int flag, bool allowForcedConnect = true);
+int WaitBlockingAdhocSocket(u64 threadSocketId, int type, int pspSocketId, void* buffer, s32_le* len, u32 timeoutUS, SceNetEtherAddr* remoteMAC, u16_le* remotePort, const char* reason);
+
+// The two transports (AdhocTransport.h). Their methods are defined next to the code they share helpers
+// with.
+class RelayTransport : public AdhocTransport {
+public:
+ int PdpCreate(const SceNetEtherAddr *saddr, int port, int bufferSize, u32 flag, bool isClient, bool enetReliable) override;
+ int PdpDelete(int id) override;
+ int PdpSendTo(int id, const SceNetEtherAddr *mac, uint16_t dport, const sockaddr_in &target, const void *data, int len, int *error) override;
+ int PdpRecv(int id, SceNetEtherAddr *saddr, uint16_t *sport, void *buf, int *len, u32 timeout, int flag) override;
+ int DoBlockingPdpRecv(AdhocSocketRequest &req, s64 &result) override;
+ void PdpUpdateRecvStat(int id, AdhocSocket *sock) override;
+ bool PdpReadyToSend(AdhocSocket *sock) override { return true; }
+ bool PdpReadyToRecv(AdhocSocket *sock) override { return true; }
+};
+
+class P2PTransport : public AdhocTransport {
+public:
+ int PdpCreate(const SceNetEtherAddr *saddr, int port, int bufferSize, u32 flag, bool isClient, bool enetReliable) override;
+ int PdpDelete(int id) override;
+ int PdpSendTo(int id, const SceNetEtherAddr *mac, uint16_t dport, const sockaddr_in &target, const void *data, int len, int *error) override;
+ int PdpRecv(int id, SceNetEtherAddr *saddr, uint16_t *sport, void *buf, int *len, u32 timeout, int flag) override;
+ int DoBlockingPdpRecv(AdhocSocketRequest &req, s64 &result) override;
+ void PdpUpdateRecvStat(int id, AdhocSocket *sock) override;
+ bool PdpReadyToSend(AdhocSocket *sock) override;
+ bool PdpReadyToRecv(AdhocSocket *sock) override;
+};
+
+static RelayTransport relayTransport;
+static P2PTransport p2pTransport;
+
+AdhocTransport &Transport() {
+ if (serverHasRelay) {
+ return relayTransport;
+ }
+ return p2pTransport;
+}
// Forward declarations for the savestate mechanism (the matching is sadly not inside its own section)
void deleteMatchingEvents(const int matchingId = -1);
@@ -472,7 +510,7 @@ static void __GameModeNotify(u64 userdata, int cyclesLate) {
if (masterGameModeArea.dataUpdated) {
int sentcount = 0;
for (auto& gma : replicaGameModeAreas) {
- if (!gma.dataSent && (serverHasRelay || IsSocketReady(sock->data.pdp.id, false, true) > 0)) {
+ if (!gma.dataSent && Transport().PdpReadyToSend(sock)) {
u16_le port = ADHOC_GAMEMODE_PORT;
auto it = gameModePeerPorts.find(gma.mac);
if (it != gameModePeerPorts.end())
@@ -536,7 +574,7 @@ static void __GameModeNotify(u64 userdata, int cyclesLate) {
}
// Recv new Replica data when available. Every member sends every period, so take all that's there.
- for (int packets = 0; packets < ADHOCCTL_GAMEMODE_MAX_MEMBERS * 2 && (serverHasRelay || IsSocketReady(sock->data.pdp.id, true, false) > 0); ++packets) {
+ for (int packets = 0; packets < ADHOCCTL_GAMEMODE_MAX_MEMBERS * 2 && Transport().PdpReadyToRecv(sock); ++packets) {
SceNetEtherAddr sendermac;
s32_le senderport = ADHOC_GAMEMODE_PORT;
s32_le bufsz = gameModeBuffSize;
@@ -1014,12 +1052,65 @@ int DoBlockingPdpRecv(AdhocSocketRequest& req, s64& result) {
result = SCE_NET_ADHOC_ERROR_SOCKET_DELETED;
return 0;
}
- auto& pdpsocket = sock->data.pdp;
if (sock->flags & ADHOC_F_ALERTRECV) {
result = SCE_NET_ADHOC_ERROR_SOCKET_ALERTED;
sock->alerted_flags |= ADHOC_F_ALERTRECV;
return 0;
}
+ return Transport().DoBlockingPdpRecv(req, result);
+}
+
+int RelayTransport::DoBlockingPdpRecv(AdhocSocketRequest &req, s64 &result) {
+ int next_size = pdp_peek_next_size_postoffice(req.id - 1);
+ if (next_size == 0) {
+ // no next packet
+ } else if (next_size > *req.length) {
+ // next is larger than current buffer
+ result = SCE_NET_ADHOC_ERROR_NOT_ENOUGH_SPACE;
+ *req.length = next_size;
+ return 0;
+ } else if (pdp_recv_postoffice(req.id - 1, req.remoteMAC, req.remotePort, req.buffer, req.length) == 0) {
+ // we got data into the request
+ result = 0;
+ return 0;
+ }
+
+ u64 now = CoreTiming::GetGlobalTimeUs();
+ if (req.timeout == 0 || now - req.startTime <= req.timeout) {
+ // Try again later
+ return -1;
+ }
+ result = SCE_NET_ADHOC_ERROR_TIMEOUT;
+ DEBUG_LOG(Log::sceNet, "sceNetAdhocPdpRecv[%i]: Socket Error (%i)", req.id, EAGAIN);
+ return 0;
+}
+
+int RelayTransport::PdpRecv(int id, SceNetEtherAddr *saddr, uint16_t *sport, void *buf, int *len, u32 timeout, int flag) {
+ int next_size = pdp_peek_next_size_postoffice(id - 1);
+ if (next_size == 0) {
+ // no next packet
+ } else if (next_size > *len) {
+ // next is larger than current buffer
+ *len = next_size;
+ return SCE_NET_ADHOC_ERROR_NOT_ENOUGH_SPACE;
+ } else if (pdp_recv_postoffice(id - 1, saddr, sport, buf, len) == 0) {
+ // we got data
+ hleEatMicro(50);
+ return 0;
+ }
+
+ if (flag == 0) {
+ // Simulate blocking behaviour with non-blocking socket
+ u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | adhocSockets[id - 1]->data.pdp.id;
+ return WaitBlockingAdhocSocket(threadSocketId, PDP_RECV, id, buf, len, timeout, saddr, sport, "pdp recv");
+ }
+
+ VERBOSE_LOG(Log::sceNet, "%08x=sceNetAdhocPdpRecv: would block", SCE_NET_ADHOC_ERROR_WOULD_BLOCK); // Temporary fix to avoid a crash on the Logs due to trying to Logs syscall's argument from another thread (ie. AdhocMatchingInput thread)
+ return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
+}
+
+int P2PTransport::DoBlockingPdpRecv(AdhocSocketRequest &req, s64 &result) {
+ auto& pdpsocket = adhocSockets[req.id - 1]->data.pdp;
int ret;
int sockerr;
@@ -1030,36 +1121,9 @@ int DoBlockingPdpRecv(AdhocSocketRequest& req, s64& result) {
sinlen = sizeof(sin);
memset(&sin, 0, sinlen);
- if (serverHasRelay) {
- while(1) {
- int next_size = pdp_peek_next_size_postoffice(req.id - 1);
- if (next_size == 0) {
- // no next packet
- ret = SOCKET_ERROR;
- sockerr = EAGAIN;
- break;
- }
- if (next_size > *req.length) {
- // next is larger than current buffer
- result = SCE_NET_ADHOC_ERROR_NOT_ENOUGH_SPACE;
- *req.length = next_size;
- return 0;
- }
- ret = pdp_recv_postoffice(req.id - 1, req.remoteMAC, req.remotePort, req.buffer, req.length);
- if (ret == 0) {
- // we got data into the request
- result = 0;
- return 0;
- }
- ret = SOCKET_ERROR;
- sockerr = EAGAIN;
- break;
- }
- } else {
- // On Windows: MSG_TRUNC are not supported on recvfrom (socket error WSAEOPNOTSUPP), so we use dummy buffer as an alternative
- ret = recvfrom(pdpsocket.id, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
- sockerr = socket_errno;
- }
+ // On Windows: MSG_TRUNC are not supported on recvfrom (socket error WSAEOPNOTSUPP), so we use dummy buffer as an alternative
+ ret = recvfrom(pdpsocket.id, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
+ sockerr = socket_errno;
// Discard packets from IP that can't be translated into MAC address to prevent confusing the game, since the sender MAC won't be updated and may contains invalid/undefined value.
// TODO: In order to discard packets from unresolvable IP (can't be translated into player's MAC) properly, we'll need to manage the socket buffer ourself,
@@ -1191,6 +1255,20 @@ static int pdp_send_postoffice(int idx, const SceNetEtherAddr *daddr, uint16_t d
return 0;
}
+int RelayTransport::PdpSendTo(int id, const SceNetEtherAddr *mac, uint16_t dport, const sockaddr_in &target, const void *data, int len, int *error) {
+ if (pdp_send_postoffice(id - 1, mac, dport, data, len) == 0) {
+ return len;
+ }
+ *error = EAGAIN;
+ return SOCKET_ERROR;
+}
+
+int P2PTransport::PdpSendTo(int id, const SceNetEtherAddr *mac, uint16_t dport, const sockaddr_in &target, const void *data, int len, int *error) {
+ int sent = sendto(adhocSockets[id - 1]->data.pdp.id, (const char *)data, len, MSG_NOSIGNAL, (const struct sockaddr *)&target, sizeof(target));
+ *error = socket_errno;
+ return sent;
+}
+
int DoBlockingPdpSend(AdhocSocketRequest& req, s64& result, AdhocSendTargets& targetPeers) {
auto sock = adhocSockets[req.id - 1];
if (!sock) {
@@ -1213,19 +1291,8 @@ int DoBlockingPdpSend(AdhocSocketRequest& req, s64& result, AdhocSendTargets& ta
target.sin_addr.s_addr = peer->ip;
target.sin_port = htons(peer->port + peer->portOffset);
- int ret = 0;
int sockerr = 0;
- if (serverHasRelay) {
- ret = pdp_send_postoffice(req.id - 1, &peer->mac, peer->port, req.buffer, targetPeers.length);
- if (ret == 0) {
- ret = targetPeers.length;
- } else {
- sockerr = EAGAIN;
- }
- } else {
- ret = sendto(pdpsocket.id, (const char*)req.buffer, targetPeers.length, MSG_NOSIGNAL, (struct sockaddr*)&target, sizeof(target));
- sockerr = socket_errno;
- }
+ int ret = Transport().PdpSendTo(req.id, &peer->mac, peer->port, target, req.buffer, targetPeers.length, &sockerr);
if (ret >= 0) {
DEBUG_LOG(Log::sceNet, "sceNetAdhocPdpSend[%i:%u](B): Sent %u bytes to %s:%u\n", req.id, getLocalPort(pdpsocket.id), ret, ip2str(target.sin_addr).c_str(), ntohs(target.sin_port));
@@ -2598,81 +2665,19 @@ static uint16_t get_unused_pdp_port() {
return 0x1000;
}
-int NetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 flag, bool postoffice_enet_pdp_reliable) {
- if (!g_Config.bEnableWlan) {
- return -1;
- }
+bool P2PTransport::PdpReadyToSend(AdhocSocket *sock) {
+ return IsSocketReady(sock->data.pdp.id, false, true) > 0;
+}
- if (!g_netInited) {
- return 0x800201CA; //PSP_LWMUTEX_ERROR_NO_SUCH_LWMUTEX
- }
+bool P2PTransport::PdpReadyToRecv(AdhocSocket *sock) {
+ return IsSocketReady(sock->data.pdp.id, true, false) > 0;
+}
- if (!netAdhocInited) {
- return SCE_NET_ADHOC_ERROR_NOT_INITIALIZED;
- }
-
- // Checked in this order on the PSP (pspautotests net/adhoc/pdp). Any buffer size is accepted.
- if (mac == NULL || flag != 0) {
- DEBUG_LOG(Log::sceNet, "%s: bad arg", __func__);
- return SCE_NET_ADHOC_ERROR_INVALID_ARG;
- }
-
- const SceNetEtherAddr *requestedMac = (const SceNetEtherAddr *)mac;
- // These three failures use up a socket ID on a PSP (pspautotests net/adhoc/pdp).
- if (adhocctlCurrentMode == ADHOCCTL_MODE_NONE || isZeroMAC(requestedMac) || (requestedMac->data[0] & 1) != 0) {
- DEBUG_LOG(Log::sceNet, "%s: invalid mac address supplied", __func__);
- SkipAdhocSocketId();
- return SCE_NET_ADHOC_ERROR_INVALID_ADDR;
- }
-
- if (port == 0xFFFF) {
- SkipAdhocSocketId();
- return SCE_NET_ADHOC_ERROR_INVALID_PORT;
- }
-
- // What the PSP does with a size under 1 isn't known; give the host one datagram's worth.
- if (bufferSize <= 0) {
- bufferSize = PSP_ADHOC_PDP_MFS;
- }
-
- if (!HasFreeAdhocSocketSlot()) {
- return SCE_NET_ADHOC_ERROR_SOCKET_ID_NOT_AVAIL;
- }
-
- if (isPDPPortInUse(port)) {
- DEBUG_LOG(Log::sceNet, "%s: port in-use", __func__);
- SkipAdhocSocketId();
- return SCE_NET_ADHOC_ERROR_PORT_IN_USE;
- }
-
- bool isClient = false;
-
- // Port 0 gets a free port in 0x1000-0x7FFF, as on a PSP (pspautotests net/adhoc/limits). Picking it
- // here rather than letting the host choose also keeps the port offset from turning it into a fixed
- // port (Phantasy Star Portable 2 creates several on port 0).
- if (port == 0) {
- isClient = true;
- port = get_unused_pdp_port();
- }
-
- SceNetEtherAddr * saddr = (SceNetEtherAddr *)mac;
- // Some games (ie. DBZ Shin Budokai 2) might be getting the saddr/srcmac content from SaveState and causing problems :( So we
- // use the local MAC instead, without writing it into the game's memory.
- SceNetEtherAddr localSrcMac;
- if (saddr != NULL) {
- getLocalMac(&localSrcMac);
- saddr = &localSrcMac;
- }
-
-
- if ((adhocctlCurrentMode == ADHOCCTL_MODE_NONE) || !isLocalMAC(saddr)) {
- DEBUG_LOG(Log::sceNet, "%s: invalid mac address supplied", __func__);
- return SCE_NET_ADHOC_ERROR_INVALID_ADDR;
- }
-
- if (serverHasRelay)
- return pdp_create_postoffice(saddr, port, bufferSize, postoffice_enet_pdp_reliable);
+int RelayTransport::PdpCreate(const SceNetEtherAddr *saddr, int port, int bufferSize, u32 flag, bool isClient, bool enetReliable) {
+ return pdp_create_postoffice(saddr, port, bufferSize, enetReliable);
+}
+int P2PTransport::PdpCreate(const SceNetEtherAddr *saddr, int port, int bufferSize, u32 flag, bool isClient, bool enetReliable) {
// Create Internet UDP Socket
// Socket is remapped through adhocSockets
int usocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
@@ -2800,6 +2805,81 @@ int NetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 flag, bool
return i + 1;
}
+int NetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 flag, bool postoffice_enet_pdp_reliable) {
+ if (!g_Config.bEnableWlan) {
+ return -1;
+ }
+
+ if (!g_netInited) {
+ return 0x800201CA; //PSP_LWMUTEX_ERROR_NO_SUCH_LWMUTEX
+ }
+
+ if (!netAdhocInited) {
+ return SCE_NET_ADHOC_ERROR_NOT_INITIALIZED;
+ }
+
+ // Checked in this order on the PSP (pspautotests net/adhoc/pdp). Any buffer size is accepted.
+ if (mac == NULL || flag != 0) {
+ DEBUG_LOG(Log::sceNet, "%s: bad arg", __func__);
+ return SCE_NET_ADHOC_ERROR_INVALID_ARG;
+ }
+
+ const SceNetEtherAddr *requestedMac = (const SceNetEtherAddr *)mac;
+ // These three failures use up a socket ID on a PSP (pspautotests net/adhoc/pdp).
+ if (adhocctlCurrentMode == ADHOCCTL_MODE_NONE || isZeroMAC(requestedMac) || (requestedMac->data[0] & 1) != 0) {
+ DEBUG_LOG(Log::sceNet, "%s: invalid mac address supplied", __func__);
+ SkipAdhocSocketId();
+ return SCE_NET_ADHOC_ERROR_INVALID_ADDR;
+ }
+
+ if (port == 0xFFFF) {
+ SkipAdhocSocketId();
+ return SCE_NET_ADHOC_ERROR_INVALID_PORT;
+ }
+
+ // What the PSP does with a size under 1 isn't known; give the host one datagram's worth.
+ if (bufferSize <= 0) {
+ bufferSize = PSP_ADHOC_PDP_MFS;
+ }
+
+ if (!HasFreeAdhocSocketSlot()) {
+ return SCE_NET_ADHOC_ERROR_SOCKET_ID_NOT_AVAIL;
+ }
+
+ if (isPDPPortInUse(port)) {
+ DEBUG_LOG(Log::sceNet, "%s: port in-use", __func__);
+ SkipAdhocSocketId();
+ return SCE_NET_ADHOC_ERROR_PORT_IN_USE;
+ }
+
+ bool isClient = false;
+
+ // Port 0 gets a free port in 0x1000-0x7FFF, as on a PSP (pspautotests net/adhoc/limits). Picking it
+ // here rather than letting the host choose also keeps the port offset from turning it into a fixed
+ // port (Phantasy Star Portable 2 creates several on port 0).
+ if (port == 0) {
+ isClient = true;
+ port = get_unused_pdp_port();
+ }
+
+ SceNetEtherAddr * saddr = (SceNetEtherAddr *)mac;
+ // Some games (ie. DBZ Shin Budokai 2) might be getting the saddr/srcmac content from SaveState and causing problems :( So we
+ // use the local MAC instead, without writing it into the game's memory.
+ SceNetEtherAddr localSrcMac;
+ if (saddr != NULL) {
+ getLocalMac(&localSrcMac);
+ saddr = &localSrcMac;
+ }
+
+
+ if ((adhocctlCurrentMode == ADHOCCTL_MODE_NONE) || !isLocalMAC(saddr)) {
+ DEBUG_LOG(Log::sceNet, "%s: invalid mac address supplied", __func__);
+ return SCE_NET_ADHOC_ERROR_INVALID_ADDR;
+ }
+
+ return Transport().PdpCreate(saddr, port, bufferSize, flag, isClient, postoffice_enet_pdp_reliable);
+}
+
/**
* Adhoc Emulator PDP Socket Creator
* @param saddr Local MAC (Unused)
@@ -2950,20 +3030,8 @@ int sceNetAdhocPdpSend(MIPSState *mips, int id, const char *mac, u32 port, void
//_acquireNetworkLock();
// Send Data. UDP are guaranteed to be sent as a whole or nothing(failed if len > SO_MAX_MSG_SIZE), and never be partially sent/recv
- int sent = 0;
int error = 0;
- if (serverHasRelay) {
- sent = pdp_send_postoffice(id - 1, daddr, dport, data, len);
- if (sent == 0) {
- sent = len;
- } else {
- sent = SOCKET_ERROR;
- error = EAGAIN;
- }
- } else {
- sent = sendto(pdpsocket.id, (const char *)data, len, MSG_NOSIGNAL, (struct sockaddr*)&target, sizeof(target));
- error = socket_errno;
- }
+ int sent = Transport().PdpSendTo(id, daddr, dport, target, data, len, &error);
if (sent == SOCKET_ERROR) {
// Simulate blocking behaviour with non-blocking socket
@@ -3064,20 +3132,8 @@ int sceNetAdhocPdpSend(MIPSState *mips, int id, const char *mac, u32 port, void
target.sin_addr.s_addr = peer.ip;
target.sin_port = htons(dport + peer.portOffset);
- int sent = 0;
int error = 0;
- if (serverHasRelay) {
- sent = pdp_send_postoffice(id - 1, &peer.mac, dport, data, len);
- if (sent == 0) {
- sent = len;
- } else {
- sent = SOCKET_ERROR;
- error = EAGAIN;
- }
- } else {
- sent = sendto(pdpsocket.id, (const char *)data, len, MSG_NOSIGNAL, (struct sockaddr*)&target, sizeof(target));
- error = socket_errno;
- }
+ int sent = Transport().PdpSendTo(id, &peer.mac, dport, target, data, len, &error);
if (sent == SOCKET_ERROR) {
DEBUG_LOG(Log::sceNet, "Socket Error (%i) on sceNetAdhocPdpSend[%i:%u->%u](BC) [size=%i]", error, id, getLocalPort(pdpsocket.id), ntohs(target.sin_port), len);
@@ -3216,171 +3272,7 @@ int sceNetAdhocPdpRecv(MIPSState *mips, int id, void *addr, void * port, void *b
return hleLogError(Log::sceNet, SCE_NET_ADHOC_ERROR_SOCKET_ALERTED, "socket alerted");
}
- // Sender Address
- struct sockaddr_in sin;
- socklen_t sinlen;
-
- // Acquire Network Lock
- //_acquireNetworkLock();
-
- SceNetEtherAddr mac;
- int received = 0;
- int error = 0;
-
- if (serverHasRelay) {
- while(1) {
- int next_size = pdp_peek_next_size_postoffice(id - 1);
- if (next_size == 0) {
- // no next packet
- received = SOCKET_ERROR;
- error = EAGAIN;
- break;
- }
- if (next_size > *len) {
- // next is larger than current buffer
- *len = next_size;
- return SCE_NET_ADHOC_ERROR_NOT_ENOUGH_SPACE;
- }
- received = pdp_recv_postoffice(id - 1, saddr, sport, buf, len);
- if (received == 0) {
- // we got data
- hleEatMicro(50);
- return 0;
- }
- received = SOCKET_ERROR;
- error = EAGAIN;
- break;
- }
- } else {
- int disCnt = 16;
- while (--disCnt > 0)
- {
- // Receive Data. PDP always sent in full size or nothing(failed), recvfrom will always receive in full size as requested (blocking) or failed (non-blocking). If available UDP data is larger than buffer, excess data is lost.
- // Should peek first for the available data size if it's more than len return SCE_NET_ADHOC_ERROR_NOT_ENOUGH_SPACE along with required size in len to prevent losing excess data
- // On Windows: MSG_TRUNC are not supported on recvfrom (socket error WSAEOPNOTSUPP), so we use dummy buffer as an alternative
- sinlen = sizeof(sin);
- memset(&sin, 0, sinlen);
- received = recvfrom(pdpsocket.id, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
- error = socket_errno;
- // Discard packets from IP that can't be translated into MAC address to prevent confusing the game, since the sender MAC won't be updated and may contains invalid/undefined value.
- // TODO: In order to discard packets from unresolvable IP (can't be translated into player's MAC) properly, we'll need to manage the socket buffer ourself,
- // by reading the whole available data, separates each datagram and discard unresolvable one, so we can calculate the correct number of available data to recv on GetPdpStat too.
- // We may also need to implement encryption (or a simple checksum will do) in order to validate the packet to findout whether it came from PPSSPP or a different App that may be sending/broadcasting data to the same port being used by a game
- // (in case the IP was resolvable but came from a different App, which will need to be discarded too)
- // Note: Looping to check too many packets (ie. contiguous) to discard per one non-blocking PdpRecv syscall may cause a slow down
- if (received != SOCKET_ERROR && !resolveIP(sin.sin_addr.s_addr, &mac)) {
- // Remove the packet from socket buffer
- sinlen = sizeof(sin);
- memset(&sin, 0, sinlen);
- recvfrom(pdpsocket.id, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
- if (flag) {
- VERBOSE_LOG(Log::sceNet, "%08x=sceNetAdhocPdpRecv: would block (disc)", SCE_NET_ADHOC_ERROR_WOULD_BLOCK); // Temporary fix to avoid a crash on the Logs due to trying to Logs syscall's argument from another thread (ie. AdhocMatchingInput thread)
- return SCE_NET_ADHOC_ERROR_WOULD_BLOCK; // hleLogSuccessVerboseX(Log::sceNet, SCE_NET_ADHOC_ERROR_WOULD_BLOCK, "would block (disc)");
- }
- else {
- // Simulate blocking behaviour with non-blocking socket, and discard more unresolvable packets until timeout reached
- u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | pdpsocket.id;
- return WaitBlockingAdhocSocket(threadSocketId, PDP_RECV, id, buf, len, timeout, saddr, sport, "pdp recv (disc)");
- }
- }
- else
- break;
- }
- }
-
- // At this point we assumed that the packet is a valid PPSSPP packet
- if (received != SOCKET_ERROR && *len < received) {
- INFO_LOG(Log::sceNet, "sceNetAdhocPdpRecv[%i:%u]: Peeked %u/%u bytes from %s:%u\n", id, getLocalPort(pdpsocket.id), received, *len, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
-
- // A datagram larger than the buffer stays queued, and isn't copied (pspautotests net/adhoc/pdp).
- // Return the actual available data size
- *len = received;
-
- // Provide Sender Information
- *saddr = mac;
- *sport = ntohs(sin.sin_port) - portOffset;
-
- // Update last recv timestamp, may cause disconnection not detected properly tho
- peerlock.lock();
- auto peer = findFriend(&mac);
- if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
- peerlock.unlock();
-
- return hleLogVerbose(Log::sceNet, SCE_NET_ADHOC_ERROR_NOT_ENOUGH_SPACE, "not enough space");
- }
-
- if (!serverHasRelay) {
- sinlen = sizeof(sin);
- memset(&sin, 0, sinlen);
- // On Windows: Socket Error 10014 may happen when buffer size is less than the minimum allowed/required (ie. negative number on Vulcanus Seek and Destroy), the address is not a valid part of the user address space (ie. on the stack or when buffer overflow occurred), or the address is not properly aligned (ie. multiple of 4 on 32bit and multiple of 8 on 64bit) https://stackoverflow.com/questions/861154/winsock-error-code-10014
- received = recvfrom(pdpsocket.id, (char*)buf, std::max(0, *len), MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
- error = socket_errno;
- }
-
- // On Windows: recvfrom on UDP can get error WSAECONNRESET when previous sendto's destination is unreachable (or destination port is not bound), may need to disable SIO_UDP_CONNRESET
- if (received == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK || error == ECONNRESET)) {
- if (flag == 0) {
- // Simulate blocking behaviour with non-blocking socket
- u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | pdpsocket.id;
- return WaitBlockingAdhocSocket(threadSocketId, PDP_RECV, id, buf, len, timeout, saddr, sport, "pdp recv");
- }
-
- VERBOSE_LOG(Log::sceNet, "%08x=sceNetAdhocPdpRecv: would block", SCE_NET_ADHOC_ERROR_WOULD_BLOCK); // Temporary fix to avoid a crash on the Logs due to trying to Logs syscall's argument from another thread (ie. AdhocMatchingInput thread)
- return SCE_NET_ADHOC_ERROR_WOULD_BLOCK; // hleLogSuccessVerboseX(Log::sceNet, SCE_NET_ADHOC_ERROR_WOULD_BLOCK, "would block");
- }
-
- hleEatMicro(50);
- // Received Data. UDP can also receives 0 data, while on TCP 0 data = connection gracefully closed, but not sure about PDP tho
- if (received >= 0) {
- DEBUG_LOG(Log::sceNet, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", id, getLocalPort(pdpsocket.id), received, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
-
- // Find Peer MAC
- if (resolveIP(sin.sin_addr.s_addr, &mac)) {
- // Provide Sender Information
- *saddr = mac;
- *sport = ntohs(sin.sin_port) - portOffset;
-
- // Save Length
- *len = received; // The PSP writes the received length back (pspautotests net/adhoc/pdp).
-
- // Update last recv timestamp, may cause disconnection not detected properly tho
- peerlock.lock();
- auto peer = findFriend(&mac);
- if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
- peerlock.unlock();
-
- // Free Network Lock
- //_freeNetworkLock();
-
- // Return Success. According to pspsdk-1.0+beta2 returned value is Number of bytes received, but JPCSP returning 0?
- return 0; // 0, not the length, as on the PSP (pspautotests net/adhoc/pdp). KH BBS can't see rooms otherwise.
- }
-
- // Free Network Lock
- //_freeNetworkLock();
-
- //free(tmpbuf);
-
- // Receiving data from unknown peer? Should never reached here! Unless the Peeked's packet was different than the Recved one (which mean there is a problem)
- WARN_LOG(Log::sceNet, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %s:%u", id, getLocalPort(pdpsocket.id), received, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
- if (flag) {
- VERBOSE_LOG(Log::sceNet, "%08x=sceNetAdhocPdpRecv: would block (problem)", SCE_NET_ADHOC_ERROR_WOULD_BLOCK); // Temporary fix to avoid a crash on the Logs due to trying to Logs syscall's argument from another thread (ie. AdhocMatchingInput thread)
- return SCE_NET_ADHOC_ERROR_WOULD_BLOCK; // hleLogSuccessVerboseX(Log::sceNet, SCE_NET_ADHOC_ERROR_WOULD_BLOCK, "would block (problem)");
- }
- }
-
- // Free Network Lock
- //_freeNetworkLock();
-
-#ifdef PDP_DIRTY_MAGIC
- // Restore Nonblocking Flag for Return Value
- if (wouldblock) flag = 1;
-#endif
-
- DEBUG_LOG(Log::sceNet, "sceNetAdhocPdpRecv[%i:%u]: Result:%i (Error:%i)", id, pdpsocket.lport, received, error);
-
- // Unexpected error (other than EAGAIN/EWOULDBLOCK/ECONNRESET) or in case the Peeked's packet was different than Recved one, treated as Timeout?
- return hleLogError(Log::sceNet, SCE_NET_ADHOC_ERROR_TIMEOUT, "timeout?");
+ return Transport().PdpRecv(id, saddr, sport, buf, len, timeout, flag);
}
// Invalid Argument
@@ -3395,6 +3287,143 @@ int sceNetAdhocPdpRecv(MIPSState *mips, int id, void *addr, void * port, void *b
return hleLogError(Log::sceNet, SCE_NET_ADHOC_ERROR_NOT_INITIALIZED, "not initialized");
}
+int P2PTransport::PdpRecv(int id, SceNetEtherAddr *saddr, uint16_t *sport, void *buf, int *len, u32 timeout, int flag) {
+ auto& pdpsocket = adhocSockets[id - 1]->data.pdp;
+
+ // Sender Address
+ struct sockaddr_in sin;
+ socklen_t sinlen;
+
+ // Acquire Network Lock
+ //_acquireNetworkLock();
+
+ SceNetEtherAddr mac;
+ int received = 0;
+ int error = 0;
+
+ int disCnt = 16;
+ while (--disCnt > 0)
+ {
+ // Receive Data. PDP always sent in full size or nothing(failed), recvfrom will always receive in full size as requested (blocking) or failed (non-blocking). If available UDP data is larger than buffer, excess data is lost.
+ // Should peek first for the available data size if it's more than len return SCE_NET_ADHOC_ERROR_NOT_ENOUGH_SPACE along with required size in len to prevent losing excess data
+ // On Windows: MSG_TRUNC are not supported on recvfrom (socket error WSAEOPNOTSUPP), so we use dummy buffer as an alternative
+ sinlen = sizeof(sin);
+ memset(&sin, 0, sinlen);
+ received = recvfrom(pdpsocket.id, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
+ error = socket_errno;
+ // Discard packets from IP that can't be translated into MAC address to prevent confusing the game, since the sender MAC won't be updated and may contains invalid/undefined value.
+ // TODO: In order to discard packets from unresolvable IP (can't be translated into player's MAC) properly, we'll need to manage the socket buffer ourself,
+ // by reading the whole available data, separates each datagram and discard unresolvable one, so we can calculate the correct number of available data to recv on GetPdpStat too.
+ // We may also need to implement encryption (or a simple checksum will do) in order to validate the packet to findout whether it came from PPSSPP or a different App that may be sending/broadcasting data to the same port being used by a game
+ // (in case the IP was resolvable but came from a different App, which will need to be discarded too)
+ // Note: Looping to check too many packets (ie. contiguous) to discard per one non-blocking PdpRecv syscall may cause a slow down
+ if (received != SOCKET_ERROR && !resolveIP(sin.sin_addr.s_addr, &mac)) {
+ // Remove the packet from socket buffer
+ sinlen = sizeof(sin);
+ memset(&sin, 0, sinlen);
+ recvfrom(pdpsocket.id, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
+ if (flag) {
+ VERBOSE_LOG(Log::sceNet, "%08x=sceNetAdhocPdpRecv: would block (disc)", SCE_NET_ADHOC_ERROR_WOULD_BLOCK); // Temporary fix to avoid a crash on the Logs due to trying to Logs syscall's argument from another thread (ie. AdhocMatchingInput thread)
+ return SCE_NET_ADHOC_ERROR_WOULD_BLOCK; // hleLogSuccessVerboseX(Log::sceNet, SCE_NET_ADHOC_ERROR_WOULD_BLOCK, "would block (disc)");
+ }
+ else {
+ // Simulate blocking behaviour with non-blocking socket, and discard more unresolvable packets until timeout reached
+ u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | pdpsocket.id;
+ return WaitBlockingAdhocSocket(threadSocketId, PDP_RECV, id, buf, len, timeout, saddr, sport, "pdp recv (disc)");
+ }
+ }
+ else
+ break;
+ }
+
+ // At this point we assumed that the packet is a valid PPSSPP packet
+ if (received != SOCKET_ERROR && *len < received) {
+ INFO_LOG(Log::sceNet, "sceNetAdhocPdpRecv[%i:%u]: Peeked %u/%u bytes from %s:%u\n", id, getLocalPort(pdpsocket.id), received, *len, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
+
+ // A datagram larger than the buffer stays queued, and isn't copied (pspautotests net/adhoc/pdp).
+ // Return the actual available data size
+ *len = received;
+
+ // Provide Sender Information
+ *saddr = mac;
+ *sport = ntohs(sin.sin_port) - portOffset;
+
+ // Update last recv timestamp, may cause disconnection not detected properly tho
+ peerlock.lock();
+ auto peer = findFriend(&mac);
+ if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
+ peerlock.unlock();
+
+ return hleLogVerbose(Log::sceNet, SCE_NET_ADHOC_ERROR_NOT_ENOUGH_SPACE, "not enough space");
+ }
+
+ sinlen = sizeof(sin);
+ memset(&sin, 0, sinlen);
+ // On Windows: Socket Error 10014 may happen when buffer size is less than the minimum allowed/required (ie. negative number on Vulcanus Seek and Destroy), the address is not a valid part of the user address space (ie. on the stack or when buffer overflow occurred), or the address is not properly aligned (ie. multiple of 4 on 32bit and multiple of 8 on 64bit) https://stackoverflow.com/questions/861154/winsock-error-code-10014
+ received = recvfrom(pdpsocket.id, (char*)buf, std::max(0, *len), MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
+ error = socket_errno;
+
+ // On Windows: recvfrom on UDP can get error WSAECONNRESET when previous sendto's destination is unreachable (or destination port is not bound), may need to disable SIO_UDP_CONNRESET
+ if (received == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK || error == ECONNRESET)) {
+ if (flag == 0) {
+ // Simulate blocking behaviour with non-blocking socket
+ u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | pdpsocket.id;
+ return WaitBlockingAdhocSocket(threadSocketId, PDP_RECV, id, buf, len, timeout, saddr, sport, "pdp recv");
+ }
+
+ VERBOSE_LOG(Log::sceNet, "%08x=sceNetAdhocPdpRecv: would block", SCE_NET_ADHOC_ERROR_WOULD_BLOCK); // Temporary fix to avoid a crash on the Logs due to trying to Logs syscall's argument from another thread (ie. AdhocMatchingInput thread)
+ return SCE_NET_ADHOC_ERROR_WOULD_BLOCK; // hleLogSuccessVerboseX(Log::sceNet, SCE_NET_ADHOC_ERROR_WOULD_BLOCK, "would block");
+ }
+
+ hleEatMicro(50);
+ // Received Data. UDP can also receives 0 data, while on TCP 0 data = connection gracefully closed, but not sure about PDP tho
+ if (received >= 0) {
+ DEBUG_LOG(Log::sceNet, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", id, getLocalPort(pdpsocket.id), received, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
+
+ // Find Peer MAC
+ if (resolveIP(sin.sin_addr.s_addr, &mac)) {
+ // Provide Sender Information
+ *saddr = mac;
+ *sport = ntohs(sin.sin_port) - portOffset;
+
+ // Save Length
+ *len = received; // The PSP writes the received length back (pspautotests net/adhoc/pdp).
+
+ // Update last recv timestamp, may cause disconnection not detected properly tho
+ peerlock.lock();
+ auto peer = findFriend(&mac);
+ if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
+ peerlock.unlock();
+
+ // Free Network Lock
+ //_freeNetworkLock();
+
+ // Return Success. According to pspsdk-1.0+beta2 returned value is Number of bytes received, but JPCSP returning 0?
+ return 0; // 0, not the length, as on the PSP (pspautotests net/adhoc/pdp). KH BBS can't see rooms otherwise.
+ }
+
+ // Free Network Lock
+ //_freeNetworkLock();
+
+ //free(tmpbuf);
+
+ // Receiving data from unknown peer? Should never reached here! Unless the Peeked's packet was different than the Recved one (which mean there is a problem)
+ WARN_LOG(Log::sceNet, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %s:%u", id, getLocalPort(pdpsocket.id), received, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
+ if (flag) {
+ VERBOSE_LOG(Log::sceNet, "%08x=sceNetAdhocPdpRecv: would block (problem)", SCE_NET_ADHOC_ERROR_WOULD_BLOCK); // Temporary fix to avoid a crash on the Logs due to trying to Logs syscall's argument from another thread (ie. AdhocMatchingInput thread)
+ return SCE_NET_ADHOC_ERROR_WOULD_BLOCK; // hleLogSuccessVerboseX(Log::sceNet, SCE_NET_ADHOC_ERROR_WOULD_BLOCK, "would block (problem)");
+ }
+ }
+
+ // Free Network Lock
+ //_freeNetworkLock();
+
+ DEBUG_LOG(Log::sceNet, "sceNetAdhocPdpRecv[%i:%u]: Result:%i (Error:%i)", id, pdpsocket.lport, received, error);
+
+ // Unexpected error (other than EAGAIN/EWOULDBLOCK/ECONNRESET) or in case the Peeked's packet was different than Recved one, treated as Timeout?
+ return hleLogError(Log::sceNet, SCE_NET_ADHOC_ERROR_TIMEOUT, "timeout?");
+}
+
int NetAdhoc_SetSocketAlert(int id, s32_le flag) {
if (id < 1 || id > MAX_SOCKET || adhocSockets[id - 1] == NULL)
return SCE_NET_ADHOC_ERROR_INVALID_SOCKET_ID;
@@ -3725,6 +3754,31 @@ static int pdp_delete_postoffice(int idx) {
return 0;
}
+int RelayTransport::PdpDelete(int id) {
+ return pdp_delete_postoffice(id - 1);
+}
+
+int P2PTransport::PdpDelete(int id) {
+ auto sock = adhocSockets[id - 1];
+
+ // Close Connection
+ shutdown(sock->data.pdp.id, SD_RECEIVE);
+ closesocket(sock->data.pdp.id);
+
+ // Remove Port Forward from Router
+ //sceNetPortClose("UDP", sock->lport);
+ //g_PortManager.Remove(IP_PROTOCOL_UDP, isOriPort ? sock->lport : sock->lport + portOffset); // Let's not remove mapping in real-time as it could cause lags/disconnection when joining a room with slow routers
+
+ // Free Memory
+ free(sock);
+
+ // Free Translation Slot
+ adhocSockets[id - 1] = NULL;
+
+ // Success
+ return 0;
+}
+
int NetAdhocPdp_Delete(int id, int unknown) {
// Library is initialized
if (netAdhocInited) {
@@ -3735,25 +3789,7 @@ int NetAdhocPdp_Delete(int id, int unknown) {
// Valid Socket
if (sock != NULL && sock->type == SOCK_PDP) {
- if (serverHasRelay)
- return pdp_delete_postoffice(id - 1);
-
- // Close Connection
- shutdown(sock->data.pdp.id, SD_RECEIVE);
- closesocket(sock->data.pdp.id);
-
- // Remove Port Forward from Router
- //sceNetPortClose("UDP", sock->lport);
- //g_PortManager.Remove(IP_PROTOCOL_UDP, isOriPort ? sock->lport : sock->lport + portOffset); // Let's not remove mapping in real-time as it could cause lags/disconnection when joining a room with slow routers
-
- // Free Memory
- free(sock);
-
- // Free Translation Slot
- adhocSockets[id - 1] = NULL;
-
- // Success
- return 0;
+ return Transport().PdpDelete(id);
}
// Invalid Socket ID
@@ -4614,6 +4650,29 @@ int sceNetAdhocTerm() {
return hleLogInfo(Log::sceNet, retval);
}
+void RelayTransport::PdpUpdateRecvStat(int id, AdhocSocket *sock) {
+ void *postofficeHandle = pdp_postoffice_recover(id - 1);
+ if (postofficeHandle != NULL) {
+ sock->data.pdp.rcv_sb_cc = pdp_buffered_data_size(postofficeHandle);
+ }
+}
+
+void P2PTransport::PdpUpdateRecvStat(int id, AdhocSocket *sock) {
+ sock->data.pdp.rcv_sb_cc = getAvailToRecv(sock->data.pdp.id, sock->buffer_size);
+ // There might be a possibility for the data to be taken by the OS, thus FIONREAD returns 0, but can be Received
+ if (sock->data.pdp.rcv_sb_cc == 0) {
+ // Let's try to peek the data size
+ // TODO: May need to filter out packets from an IP that can't be translated to MAC address
+ struct sockaddr_in sin;
+ socklen_t sinlen;
+ sinlen = sizeof(sin);
+ memset(&sin, 0, sinlen);
+ int received = recvfrom(sock->data.pdp.id, dummyPeekBuf64k, std::min((u32)dummyPeekBuf64kSize, sock->buffer_size), MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
+ if (received > 0)
+ sock->data.pdp.rcv_sb_cc = received;
+ }
+}
+
static int sceNetAdhocGetPdpStat(MIPSState *mips, u32 structSize, u32 structAddr) {
VERBOSE_LOG(Log::sceNet, "sceNetAdhocGetPdpStat(%08x, %08x) at %08x", structSize, structAddr, mips->pc);
@@ -4658,26 +4717,7 @@ static int sceNetAdhocGetPdpStat(MIPSState *mips, u32 structSize, u32 structAddr
// Set available bytes to be received. With FIONREAD There might be ghosting 1 byte in recv buffer when remote peer's socket got closed (ie. Warriors Orochi 2) Attempting to recv this ghost 1 byte will result to socket error 10054 (may need to disable SIO_UDP_CONNRESET error)
// It seems real PSP respecting the socket buffer size arg, so we may need to cap the value up to the buffer size arg since we use larger buffer, for PDP/UDP the total size must not contains partial/truncated message to avoid data loss.
// TODO: We may need to manage PDP messages ourself by reading each msg 1-by-1 and moving it to our internal buffer(msg array) in order to calculate the correct messages size that can fit into buffer size when there are more than 1 messages in the recv buffer (simulate FIONREAD)
- if (serverHasRelay) {
- void *postofficeHandle = pdp_postoffice_recover(j);
- if (postofficeHandle != NULL) {
- sock->data.pdp.rcv_sb_cc = pdp_buffered_data_size(postofficeHandle);
- }
- } else {
- sock->data.pdp.rcv_sb_cc = getAvailToRecv(sock->data.pdp.id, sock->buffer_size);
- // There might be a possibility for the data to be taken by the OS, thus FIONREAD returns 0, but can be Received
- if (sock->data.pdp.rcv_sb_cc == 0) {
- // Let's try to peek the data size
- // TODO: May need to filter out packets from an IP that can't be translated to MAC address
- struct sockaddr_in sin;
- socklen_t sinlen;
- sinlen = sizeof(sin);
- memset(&sin, 0, sinlen);
- int received = recvfrom(sock->data.pdp.id, dummyPeekBuf64k, std::min((u32)dummyPeekBuf64kSize, sock->buffer_size), MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
- if (received > 0)
- sock->data.pdp.rcv_sb_cc = received;
- }
- }
+ Transport().PdpUpdateRecvStat(j + 1, sock);
// Copy Socket Data from Internal Memory
memcpy(&buf[i], &sock->data.pdp, sizeof(SceNetAdhocPdpStat));
diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj
index 9a39a21b67..e6fc677e07 100644
--- a/UWP/CoreUWP/CoreUWP.vcxproj
+++ b/UWP/CoreUWP/CoreUWP.vcxproj
@@ -182,6 +182,7 @@
+
diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters
index c62fc9ce84..c2e0505c0c 100644
--- a/UWP/CoreUWP/CoreUWP.vcxproj.filters
+++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters
@@ -512,6 +512,7 @@
+