diff --git a/Common/Net/HTTPClient.cpp b/Common/Net/HTTPClient.cpp index 2b7ec91b4116..2f2d6c7a648f 100644 --- a/Common/Net/HTTPClient.cpp +++ b/Common/Net/HTTPClient.cpp @@ -225,6 +225,7 @@ constexpr const char *HTTP_VERSION = "1.1"; Client::Client() { userAgent_ = DEFAULT_USERAGENT; + httpVersion_ = HTTP_VERSION; } Client::~Client() { @@ -368,7 +369,7 @@ int Client::SendRequestWithData(const char *method, const RequestParams &req, co return 0; } -int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, net::RequestProgress *progress) { +int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, net::RequestProgress *progress, std::string *statusLine) { // Snarf all the data we can into RAM. A little unsafe but hey. static constexpr float CANCEL_INTERVAL = 0.25f; bool ready = false; @@ -406,6 +407,9 @@ int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector & return -1; } + if (statusLine) + *statusLine = line; + while (true) { int sz = readbuf->TakeLineCRLF(&line); if (!sz || sz < 0) diff --git a/Common/Net/HTTPClient.h b/Common/Net/HTTPClient.h index 619ab80423b6..676e7fa6834c 100644 --- a/Common/Net/HTTPClient.h +++ b/Common/Net/HTTPClient.h @@ -72,7 +72,7 @@ class Client : public net::Connection { int SendRequest(const char *method, const RequestParams &req, const char *otherHeaders, net::RequestProgress *progress); int SendRequestWithData(const char *method, const RequestParams &req, const std::string &data, const char *otherHeaders, net::RequestProgress *progress); - int ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, net::RequestProgress *progress); + int ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, net::RequestProgress *progress, std::string *statusLine = nullptr); // If your response contains a response, you must read it. int ReadResponseEntity(net::Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, net::RequestProgress *progress); @@ -84,8 +84,13 @@ class Client : public net::Connection { userAgent_ = value; } + void SetHttpVersion(const char* version) { + httpVersion_ = version; + } + protected: std::string userAgent_; + const char* httpVersion_; double dataTimeout_ = 900.0; }; diff --git a/Core/Config.cpp b/Core/Config.cpp index 27f6a0cf25f1..a72e22f86995 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -857,6 +857,8 @@ static const ConfigSetting networkSettings[] = { ConfigSetting("EnableAdhocServer", &g_Config.bEnableAdhocServer, false, CfgFlag::PER_GAME), ConfigSetting("proAdhocServer", &g_Config.proAdhocServer, "socom.cc", CfgFlag::PER_GAME), ConfigSetting("PortOffset", &g_Config.iPortOffset, 10000, CfgFlag::PER_GAME), + ConfigSetting("PrimaryDNSServer", &g_Config.primaryDNSServer, "135.148.144.253", CfgFlag::PER_GAME), + ConfigSetting("SecondaryDNSServer", &g_Config.secondaryDNSServer, "0.0.0.0", CfgFlag::PER_GAME), ConfigSetting("MinTimeout", &g_Config.iMinTimeout, 0, CfgFlag::PER_GAME), ConfigSetting("ForcedFirstConnect", &g_Config.bForcedFirstConnect, false, CfgFlag::PER_GAME), ConfigSetting("EnableUPnP", &g_Config.bEnableUPnP, false, CfgFlag::PER_GAME), diff --git a/Core/Config.h b/Core/Config.h index 8c7c9ef5f8b8..9ad88008f373 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -436,6 +436,8 @@ struct Config { // Networking std::string proAdhocServer; + std::string primaryDNSServer; + std::string secondaryDNSServer; bool bEnableWlan; bool bEnableAdhocServer; bool bEnableUPnP; diff --git a/Core/Dialog/PSPMsgDialog.cpp b/Core/Dialog/PSPMsgDialog.cpp index 6b23fde60b64..821e25aca669 100755 --- a/Core/Dialog/PSPMsgDialog.cpp +++ b/Core/Dialog/PSPMsgDialog.cpp @@ -32,7 +32,7 @@ static const float FONT_SCALE = 0.65f; // These are rough, it seems to take a long time to init, and probably depends on threads. // TODO: This takes like 700ms on a PSP but that's annoyingly long. -const static int MSG_INIT_DELAY_US = 300000; +const static int MSG_INIT_DELAY_US = 350000; const static int MSG_SHUTDOWN_DELAY_US = 26000; PSPMsgDialog::PSPMsgDialog(UtilityDialogType type) : PSPDialog(type) { diff --git a/Core/Dialog/PSPNetconfDialog.cpp b/Core/Dialog/PSPNetconfDialog.cpp index 55143dcf4f95..8ad335b142aa 100644 --- a/Core/Dialog/PSPNetconfDialog.cpp +++ b/Core/Dialog/PSPNetconfDialog.cpp @@ -44,7 +44,7 @@ static const float FONT_SCALE = 0.65f; // Needs testing. const static int NET_INIT_DELAY_US = 200000; -const static int NET_SHUTDOWN_DELAY_US = 200000; +const static int NET_SHUTDOWN_DELAY_US = 501000; const static int NET_CONNECT_TIMEOUT = 15000000; // Using 15 secs to match the timeout on Adhoc Server side (SERVER_USER_TIMEOUT) struct ScanInfos { @@ -79,6 +79,7 @@ int PSPNetconfDialog::Init(u32 paramAddr) { connResult = -1; scanInfosAddr = 0; scanStep = 0; + canceling = false; startTime = (u64)(time_now_d() * 1000000.0); StartFade(true); @@ -244,6 +245,7 @@ int PSPNetconfDialog::Update(int animSpeed) { // It seems JPCSP doesn't check for NETCONF_STATUS_APNET if (request.netAction == NETCONF_CONNECT_APNET || request.netAction == NETCONF_STATUS_APNET || request.netAction == NETCONF_CONNECT_APNET_LAST) { int state = NetApctl_GetState(); + bool timedout = (state == PSP_NET_APCTL_STATE_DISCONNECTED && now - startTime > NET_CONNECT_TIMEOUT); UpdateFade(animSpeed); StartDraw(); @@ -285,33 +287,69 @@ int PSPNetconfDialog::Update(int animSpeed) { DrawBanner(); DrawIndicator(); - if (state == PSP_NET_APCTL_STATE_GOT_IP || state == PSP_NET_APCTL_STATE_GETTING_IP) { - DisplayMessage(di->T("ObtainingIP", "Obtaining IP address.\nPlease wait..."), di->T("ConnectionName", "Connection Name"), netApctlInfo.name, di->T("SSID"), netApctlInfo.ssid); + if (!g_Config.bEnableWlan) { + DisplayMessage(di->T("WlanIsOff", "A connection error has occurred.\nThe WIRELESS switch on the PPSSPP system is not turned on."), di->T("ConnectionName", "Connection Name"), netApctlInfo.name, di->T("SSID"), netApctlInfo.ssid); + DisplayButtons(DS_BUTTON_CANCEL, di->T("Back")); + } + else if (timedout) { + // FIXME: Do we need to show error message on timeout? + DisplayMessage(di->T("InternalError", "An internal error has occurred.") + StringFromFormat("\n(%08X)", connResult)); + DisplayButtons(DS_BUTTON_CANCEL, di->T("Back")); + } + else if (canceling) { + DisplayMessage(di->T("CancelingPleaseWait", "Canceling.\nPlease wait..."), di->T("ConnectionName", "Connection Name"), netApctlInfo.name, di->T("SSID"), netApctlInfo.ssid); } else { - // Skipping the Select Connection screen since we only have 1 fake profile - DisplayMessage(di->T("ConnectingAP", "Connecting to the access point.\nPlease wait..."), di->T("ConnectionName", "Connection Name"), netApctlInfo.name, di->T("SSID"), netApctlInfo.ssid); + + if (state == PSP_NET_APCTL_STATE_GOT_IP || state == PSP_NET_APCTL_STATE_GETTING_IP) { + DisplayMessage(di->T("ObtainingIP", "Obtaining IP address.\nPlease wait..."), di->T("ConnectionName", "Connection Name"), netApctlInfo.name, di->T("SSID"), netApctlInfo.ssid); + } + else { + // Skipping the Select Connection screen since we only have 1 fake profile + DisplayMessage(di->T("ConnectingAP", "Connecting to the access point.\nPlease wait..."), di->T("ConnectionName", "Connection Name"), netApctlInfo.name, di->T("SSID"), netApctlInfo.ssid); + DisplayButtons(DS_BUTTON_CANCEL, di->T("Cancel")); + } + + // The Netconf dialog stays visible until the network reaches the state PSP_NET_APCTL_STATE_GOT_IP. + if (state == PSP_NET_APCTL_STATE_GOT_IP) { + if (pendingStatus != SCE_UTILITY_STATUS_FINISHED) { + StartFade(false); + ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US); + } + } + + else if (state == PSP_NET_APCTL_STATE_JOINING) { + // Switch to the next (Obtaining IP) message animation + // TODO: Should be animating the top and bottom lines widening-from-the-middle animation instead of fading-out and in + //StartFade(true); + } + + else if (state == PSP_NET_APCTL_STATE_DISCONNECTED) { + // FIXME: Based on JPCSP+official prx, sceNetApctl* calls supposed to runs on SceDialogmainGraphics thread instead of current thread where Update is being called (ie. Main), thus animation smoothness won't be affected by blocking syscalls + // When connecting with infrastructure, simulate a connection using the first network configuration entry. + if (connResult < 0 && (now - startTime > 2000000)) { + connResult = sceNetApctlConnect(1); + } + } } - DisplayButtons(DS_BUTTON_CANCEL, di->T("Cancel")); - // The Netconf dialog stays visible until the network reaches the state PSP_NET_APCTL_STATE_GOT_IP. + // The Netconf dialog stays visible until the network reaches the state PSP_NET_APCTL_STATE_GOT_IP. if (state == PSP_NET_APCTL_STATE_GOT_IP) { + // Checking pendingStatus to make sure ChangeStatus not to continously extending the delay ticks on every call for eternity if (pendingStatus != SCE_UTILITY_STATUS_FINISHED) { StartFade(false); ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US); } } - else if (state == PSP_NET_APCTL_STATE_JOINING) { - // Switch to the next message - StartFade(true); - } - - else if (state == PSP_NET_APCTL_STATE_DISCONNECTED) { - // When connecting with infrastructure, simulate a connection using the first network configuration entry. - if (connResult < 0) { - connResult = sceNetApctlConnect(1); + if (!canceling && state != PSP_NET_APCTL_STATE_GOT_IP && state != PSP_NET_APCTL_STATE_GETTING_IP && IsButtonPressed(cancelButtonFlag)) { + if (state != PSP_NET_APCTL_STATE_DISCONNECTED) { + canceling = true; + sceNetApctlDisconnect(); } + StartFade(false); + ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US); + request.common.result = SCE_UTILITY_DIALOG_RESULT_ABORT; } } @@ -320,6 +358,9 @@ int PSPNetconfDialog::Update(int animSpeed) { else if (request.netAction == NETCONF_CONNECT_ADHOC || request.netAction == NETCONF_CREATE_ADHOC || request.netAction == NETCONF_JOIN_ADHOC) { int state = NetAdhocctl_GetState(); bool timedout = (state == ADHOCCTL_STATE_DISCONNECTED && now - startTime > NET_CONNECT_TIMEOUT); + std::string channel = std::to_string(g_Config.iWlanAdhocChannel); + if (g_Config.iWlanAdhocChannel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) + channel = "Automatic"; UpdateFade(animSpeed); StartDraw(); @@ -327,16 +368,19 @@ int PSPNetconfDialog::Update(int animSpeed) { DrawBanner(); DrawIndicator(); - if (timedout) { - // FIXME: Do we need to show error message? + if (!g_Config.bEnableWlan) { + DisplayMessage(di->T("WlanIsOff", "A connection error has occurred.\nThe WIRELESS switch on the PPSSPP system is not turned on.")); + DisplayButtons(DS_BUTTON_CANCEL, di->T("Back")); + } + else if (timedout) { + // FIXME: Do we need to show error message on timeout? DisplayMessage(di->T("InternalError", "An internal error has occurred.") + StringFromFormat("\n(%08X)", connResult)); DisplayButtons(DS_BUTTON_CANCEL, di->T("Back")); } + else if (canceling) { + DisplayMessage(di->T("CancelingPleaseWait", "Canceling.\nPlease wait..."), di->T("Channel:") + std::string(" ") + di->T(channel)); + } else { - std::string channel = std::to_string(g_Config.iWlanAdhocChannel); - if (g_Config.iWlanAdhocChannel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) - channel = "Automatic"; - DisplayMessage(di->T("ConnectingPleaseWait", "Connecting.\nPlease wait..."), di->T("Channel:") + std::string(" ") + di->T(channel)); // Only Join mode is showing Cancel button on KHBBS and the button will fade out before the dialog is fading out, probably because it's already connected thus can't be canceled anymore @@ -345,6 +389,7 @@ int PSPNetconfDialog::Update(int animSpeed) { // KHBBS will first enter the arena using NETCONF_CONNECT_ADHOC (auto-create group when not exist yet?), but when the event started the event's creator use NETCONF_CREATE_ADHOC while the joining players use NETCONF_JOIN_ADHOC if (request.NetconfData.IsValid()) { + // FIXME: Based on JPCSP+official prx, sceNetAdhocctl* calls supposed to runs on SceDialogmainGraphics thread instead of current thread where Update is being called (ie. Main), thus animation smoothness won't be affected by blocking syscalls if (state == ADHOCCTL_STATE_DISCONNECTED) { switch (request.netAction) { @@ -441,7 +486,11 @@ int PSPNetconfDialog::Update(int animSpeed) { scanInfosAddr = 0; } - if ((request.netAction == NETCONF_JOIN_ADHOC || timedout) && IsButtonPressed(cancelButtonFlag)) { + if ((request.netAction == NETCONF_JOIN_ADHOC || timedout || !g_Config.bEnableWlan) && IsButtonPressed(cancelButtonFlag)) { + if (state != ADHOCCTL_STATE_DISCONNECTED) { + canceling = true; + sceNetAdhocctlDisconnect(); + } StartFade(false); ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US); request.common.result = SCE_UTILITY_DIALOG_RESULT_ABORT; @@ -454,9 +503,6 @@ int PSPNetconfDialog::Update(int animSpeed) { EndDraw(); } - if (ReadStatus() == SCE_UTILITY_STATUS_FINISHED || pendingStatus == SCE_UTILITY_STATUS_FINISHED) - Memory::Memcpy(requestAddr, &request, request.common.size, "NetConfDialogParam"); - return 0; } @@ -469,6 +515,10 @@ int PSPNetconfDialog::Shutdown(bool force) { ChangeStatusShutdown(NET_SHUTDOWN_DELAY_US); } + // FIXME: This should probably be done within FinishShutdown? since FinishShutdown is not overrideable, Shutdown is the closes method + if (Memory::IsValidAddress(requestAddr)) // Need to validate first to prevent Invalid address when the game is being Shutdown/Exited to menu + Memory::Memcpy(requestAddr, &request, request.common.size, "NetConfDialogParam"); + return 0; } diff --git a/Core/Dialog/PSPNetconfDialog.h b/Core/Dialog/PSPNetconfDialog.h index e2d06f3739d7..95ed0a2caaf6 100644 --- a/Core/Dialog/PSPNetconfDialog.h +++ b/Core/Dialog/PSPNetconfDialog.h @@ -60,6 +60,7 @@ class PSPNetconfDialog: public PSPDialog { u32 requestAddr = 0; int connResult = -1; bool hideNotice = false; + bool canceling = false; int yesnoChoice = 0; float scrollPos_ = 0.0f; diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index d745818338d5..bb99da3116f9 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -232,6 +232,16 @@ template void WrapI_UUUUU() { RETURN(retval); } +template void WrapI_UCUUI() { + int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3), PARAM(4)); + RETURN(retval); +} + +template void WrapI_UICUU() { + int retval = func(PARAM(0), PARAM(1), Memory::GetCharPointer(PARAM(2)), PARAM(3), PARAM(4)); + RETURN(retval); +} + template void WrapI_V() { int retval = func(); RETURN(retval); @@ -438,11 +448,21 @@ template void WrapI_IIIUI() { RETURN(retval); } +template void WrapI_IIIUU() { + int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); + RETURN(retval); +} + template void WrapI_IUUII() { int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); RETURN(retval); } +template void WrapI_IUUIII() { + int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5)); + RETURN(retval); +} + template void WrapI_ICIUU() { int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3), PARAM(4)); RETURN(retval); @@ -483,6 +503,11 @@ template void WrapI_CU() { RETURN(retval); } +template void WrapI_ICU() { + int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2)); + RETURN(retval); +} + template void WrapI_CUI() { int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2)); RETURN(retval); @@ -755,6 +780,16 @@ template void WrapI_IUUU() { RETURN(retval); } +template void WrapI_IUIIUU() { + int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5)); + RETURN(retval); +} + +template void WrapI_IUIIUI() { + int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5)); + RETURN(retval); +} + template void WrapI_IUU() { int retval = func(PARAM(0), PARAM(1), PARAM(2)); RETURN(retval); @@ -765,6 +800,11 @@ template void WrapU_UUUUUUU() { RETURN(retval); } +template void WrapI_UUUUUUU() { + int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6)); + RETURN(retval); +} + template void WrapI_IUUUUUU() { int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6)); RETURN(retval); diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index 0e0999c0f91c..2169d44aaba0 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -53,6 +53,7 @@ #include "sceNet.h" #include "sceNetAdhoc.h" #include "sceNp.h" +#include "sceNp2.h" #include "sceMpeg.h" #include "sceOpenPSID.h" #include "sceP3da.h" @@ -307,6 +308,8 @@ void RegisterAllModules() { Register_sceDdrdb(); Register_mp4msv(); Register_InterruptManagerForKernel(); + Register_sceNpMatching2(); + Register_sceNetApctl_internal_user(); // add new modules here. } diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index dc7ca03d4df7..2150387c0e87 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -78,7 +78,7 @@ uint16_t portOffset; uint32_t minSocketTimeoutUS; -uint32_t fakePoolSize = 0; +SceNetMallocStat netAdhocPoolStat = {}; SceNetAdhocMatchingContext * contexts = NULL; char* dummyPeekBuf64k = NULL; int dummyPeekBuf64kSize = 65536; @@ -1496,8 +1496,8 @@ int friendFinder(){ SceNetAdhocctlConnectBSSIDPacketS2C* packet = (SceNetAdhocctlConnectBSSIDPacketS2C*)rx; INFO_LOG(SCENET, "FriendFinder: Incoming OPCODE_CONNECT_BSSID [%s]", mac2str(&packet->mac).c_str()); - // Update User BSSID - parameter.bssid.mac_addr = packet->mac; // This packet seems to contains Adhoc Group Creator's BSSID (similar to AP's BSSID) so it shouldn't get mixed up with local MAC address + // Update Group BSSID + parameter.bssid.mac_addr = packet->mac; // This packet seems to contains Adhoc Group Creator's BSSID (similar to AP's BSSID) so it shouldn't get mixed up with local MAC address. Note: On JPCSP + prx files params.bssid is hardcoded to "Jpcsp\0" and doesn't match to any of player's mac // 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?) // Notify Event Handlers @@ -1915,10 +1915,22 @@ bool isPrivateIP(uint32_t ip) { return false; } +bool isAPIPA(uint32_t ip) { + return (((uint8_t*)&ip)[0] == 169 && ((uint8_t*)&ip)[1] == 254); +} + bool isLoopbackIP(uint32_t ip) { return ((uint8_t*)&ip)[0] == 0x7f; } +bool isMulticastIP(uint32_t ip) { + return ((ip & 0xF0) == 0xE0); +} + +bool isBroadcastIP(uint32_t ip, const uint32_t subnetmask) { + return (ip == (ip | (~subnetmask))); +} + void getLocalMac(SceNetEtherAddr * addr){ // Read MAC Address from config uint8_t mac[ETHER_ADDR_LEN] = {0}; @@ -1972,14 +1984,18 @@ int getSockMaxSize(int udpsock) { } int getSockBufferSize(int sock, int opt) { // opt = SO_RCVBUF/SO_SNDBUF - int n = PSP_ADHOC_PDP_MFS; // 16384; + int n = PSP_ADHOC_PDP_MFS*2; // 16384; // The value might be twice of the value being set using setsockopt socklen_t m = sizeof(n); - getsockopt(sock, SOL_SOCKET, opt, (char *)&n, &m); // in linux the value is twice of the value being set using setsockopt - return (n/2); + getsockopt(sock, SOL_SOCKET, opt, (char *)&n, &m); + return (n); } int setSockBufferSize(int sock, int opt, int size) { // opt = SO_RCVBUF/SO_SNDBUF - int n = size; // 8192; //16384 + int n = size; // 8192; + switch (opt) { + case SO_RCVBUF: n = std::max(size, 128); break; // FIXME: The minimum (doubled) value for SO_RCVBUF is 256 ? (2048+MTU+padding on newer OS? TCP_SKB_MIN_TRUESIZE) + case SO_SNDBUF: n = std::max(size, 1024); break; // FIXME: The minimum (doubled) value for SO_SNDBUF is 2048 ? (twice the minimum of SO_RCVBUF on newer OS? TCP_SKB_MIN_TRUESIZE * 2) + } return setsockopt(sock, SOL_SOCKET, opt, (char *)&n, sizeof(n)); } diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 49ab5c8e4806..32f34b37865d 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -70,6 +70,22 @@ #undef EALREADY #undef ETIMEDOUT #undef EOPNOTSUPP +#undef ENOTSOCK +#undef EPROTONOSUPPORT +#undef ESOCKTNOSUPPORT +#undef EPFNOSUPPORT +#undef EAFNOSUPPORT +#undef EINTR +#undef EACCES +#undef EFAULT +#undef EINVAL +#undef ENOSPC +#undef EHOSTDOWN +#undef EADDRINUSE +#undef EADDRNOTAVAIL +#undef ENETUNREACH +#undef EHOSTUNREACH +#undef ENETDOWN #define errno WSAGetLastError() #define ESHUTDOWN WSAESHUTDOWN #define ECONNABORTED WSAECONNABORTED @@ -84,6 +100,22 @@ #define EALREADY WSAEALREADY #define ETIMEDOUT WSAETIMEDOUT #define EOPNOTSUPP WSAEOPNOTSUPP +#define ENOTSOCK WSAENOTSOCK +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#define EINTR WSAEINTR +#define EACCES WSAEACCES +#define EFAULT WSAEFAULT +#define EINVAL WSAEINVAL +#define ENOSPC ERROR_INVALID_PARAMETER +#define EHOSTDOWN WSAEHOSTDOWN +#define EADDRINUSE WSAEADDRINUSE +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#define ENETUNREACH WSAENETUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#define ENETDOWN WSAENETDOWN inline bool connectInProgress(int errcode){ return (errcode == WSAEWOULDBLOCK || errcode == WSAEINPROGRESS || errcode == WSAEALREADY || errcode == WSAEINVAL); } // WSAEINVAL should be treated as WSAEALREADY during connect for backward-compatibility with Winsock 1.1 inline bool isDisconnected(int errcode) { return (errcode == WSAECONNRESET || errcode == WSAECONNABORTED || errcode == WSAESHUTDOWN); } #else @@ -681,6 +713,10 @@ enum { const size_t MAX_ADHOCCTL_HANDLERS = 32; //4 const size_t MAX_MATCHING_HANDLERS = 32; //4 +#define PSP_NET_ADHOC_MATCHING_MAXNUM 16 +#define PSP_NET_ADHOC_MATCHING_MAXOPTLEN 65511 +#define PSP_NET_ADHOC_MATCHING_MAXHELLOOPTLEN 65503 + enum { /** * Matching events used in pspAdhocMatchingCallback @@ -943,7 +979,7 @@ extern SockAddrIN4 g_localhostIP; // Used to differentiate localhost IP on multi extern sockaddr LocalIP; // IP of Network Adapter used to connect to Adhoc Server (LAN/WAN) extern int defaultWlanChannel; // Default WLAN Channel for Auto, JPCSP uses 11 -extern uint32_t fakePoolSize; +extern SceNetMallocStat netAdhocPoolStat; extern SceNetAdhocMatchingContext * contexts; extern char* dummyPeekBuf64k; extern int dummyPeekBuf64kSize; @@ -1304,13 +1340,29 @@ uint32_t getLocalIp(int sock); /* * Check if an IP (big-endian/network order) is Private or Public IP */ +bool isMulticastIP(uint32_t ip); + +/* + * Check if an IP (big-endian/network order) is a Multicast IP + */ bool isPrivateIP(uint32_t ip); +/* + * Check if an IP (big-endian/network order) is APIPA(169.254.x.x) IP + */ +bool isAPIPA(uint32_t ip); + /* * Check if an IP (big-endian/network order) is Loopback IP */ bool isLoopbackIP(uint32_t ip); +/* + * Check if an IP (big-endian/network order) is a Broadcast IP + * Default subnet mask is 255.255.255.0 + */ +bool isBroadcastIP(uint32_t ip, const uint32_t subnetmask = 0x00ffffff); + /* * Get Number of bytes available in buffer to be Received * @param sock fd @@ -1325,11 +1377,23 @@ int getSockMaxSize(int udpsock); /* * Get Socket Buffer Size (opt = SO_RCVBUF/SO_SNDBUF) + * Note: The value might be twice of the value being set using setsockopt */ int getSockBufferSize(int sock, int opt); /* * Set Socket Buffer Size (opt = SO_RCVBUF/SO_SNDBUF) +* Notes: +* SO_RCVBUF: Sets or gets the maximum socket receive buffer in bytes. +* The kernel doubles this value (to allow space for +* bookkeeping overhead) when it is set using setsockopt(2), +* and this doubled value is returned by getsockopt(2). +* The minimum (doubled) value for this option is 256. +* SO_SNDBUF: Sets or gets the maximum socket send buffer in bytes. +* The kernel doubles this value (to allow space for +* bookkeeping overhead) when it is set using setsockopt(2), +* and this doubled value is returned by getsockopt(2). +* The minimum (doubled) value for this option is 2048. */ int setSockBufferSize(int sock, int opt, int size); diff --git a/Core/HLE/sceHttp.cpp b/Core/HLE/sceHttp.cpp index 6d13b6f1f2f9..d1a1eb7c27c6 100644 --- a/Core/HLE/sceHttp.cpp +++ b/Core/HLE/sceHttp.cpp @@ -15,68 +15,368 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include +#include +#include +#include +#include + +#include "Core/Core.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" - +#include "Core/HLE/sceKernelMemory.h" #include "Core/HLE/sceHttp.h" +#include "Core/Debugger/MemBlockInfo.h" +#include "Common/StringUtils.h" +#include "Common/LogReporting.h" +#include "Common/Net/URL.h" #include "Common/Net/HTTPClient.h" -// If http isn't loaded (seems unlikely), most functions should return SCE_KERNEL_ERROR_LIBRARY_NOTFOUND - -// Could come in handy someday if we ever implement sceHttp* for real. -enum PSPHttpMethod { - PSP_HTTP_METHOD_GET, - PSP_HTTP_METHOD_POST, - PSP_HTTP_METHOD_HEAD -}; +static std::vector> httpObjects; +static std::mutex httpLock; -// Just a holder for settings like user agent string -class HTTPTemplate { - char useragent[512]; -}; +bool httpInited = false; +bool httpsInited = false; +bool httpCacheInited = false; -class HTTPConnection { -}; +HTTPTemplate::HTTPTemplate(const char* userAgent, int httpVer, int autoProxyConf) { + this->userAgent = userAgent ? userAgent : ""; + this->httpVer = (SceHttpVersion)httpVer; + this->autoProxyConf = (SceHttpProxyMode)autoProxyConf; +} -class HTTPRequest { +int HTTPTemplate::addRequestHeader(const char* name, const char* value, u32 mode) { + // Note: std::map doesn't support key duplication, will need std::multimap to support SCE_HTTP_HEADER_ADD mode + //if (mode != SCE_HTTP_HEADER_OVERWRITE) + // return SCE_HTTP_ERROR_NOT_SUPPORTED; // FIXME: PSP might not support mode other than SCE_HTTP_HEADER_OVERWRITE (0) -}; + // Handle User-Agent separately, since PSP Browser seems to add "User-Agent" header manually + if (mode == SCE_HTTP_HEADER_OVERWRITE) { + std::string s = name; + std::transform(s.begin(), s.end(), s.begin(), [](const unsigned char i) { return std::tolower(i); }); + if (s == "user-agent") + setUserAgent(value); + } + requestHeaders_[name] = value; + return 0; +} -int sceHttpSetResolveRetry(int connectionID, int retryCount) { - ERROR_LOG(SCENET, "UNIMPL sceHttpSetResolveRetry(%d, %d)", connectionID, retryCount); +int HTTPTemplate::removeRequestHeader(const char* name) { + requestHeaders_.erase(name); return 0; } -static int sceHttpInit(int unknown) { - ERROR_LOG(SCENET, "UNIMPL sceHttpInit(%i)", unknown); +HTTPConnection::HTTPConnection(int templateID, const char* hostString, const char* scheme, u32 port, int enableKeepalive) { + // Copy base data as initial base value for this + HTTPTemplate::operator=(*httpObjects[templateID - 1LL]); + + // Initialize + this->templateID = templateID; + this->hostString = hostString; + this->scheme = scheme; + this->port = port; + this->enableKeepalive = enableKeepalive; +} + +HTTPRequest::HTTPRequest(int connectionID, int method, const char* url, u64 contentLength) { + // Copy base data as initial base value for this + // Since dynamic_cast/dynamic_pointer_cast/typeid requires RTTI to be enabled (ie. /GR instead of /GR- on msvc, enabled by default on most compilers), so we can only use static_cast here + HTTPConnection::operator=(static_cast(*httpObjects[connectionID - 1LL])); + + // Initialize + this->connectionID = connectionID; + this->method = method; + this->url = url ? url : ""; + this->contentLength = contentLength; + + //progress_.cancelled = &cancelled_; + responseContent_.clear(); +} + +HTTPRequest::~HTTPRequest() { + client.Disconnect(); + if (Memory::IsValidAddress(headerAddr_)) + userMemory.Free(headerAddr_); +} + +int HTTPRequest::getResponseContentLength() { + // FIXME: Will sceHttpGetContentLength returns an error if the request was not sent yet? + //if (progress_.progress == 0.0f) + // return SCE_HTTP_ERROR_BEFORE_SEND; + + entityLength_ = -1; // FIXME: should we default to 0 instead? + for (std::string& line : responseHeaders_) { + if (startsWithNoCase(line, "Content-Length")) { + size_t pos = line.find_first_of(':'); + if (pos != line.npos) { + pos++; + entityLength_ = atoi(&line[pos]); + } + } + } + return entityLength_; +} + +int HTTPRequest::abortRequest() { + cancelled_ = true; + // FIXME: Will sceHttpAbortRequest returns an error if the request was not sent yet? + //if (progress_.progress == 0.0f) + // return SCE_HTTP_ERROR_BEFORE_SEND; + return 0; +} + +int HTTPRequest::getStatusCode() { + // FIXME: Will sceHttpGetStatusCode returns an error if the request was not sent yet? + //if (progress_.progress == 0.0f) + // return SCE_HTTP_ERROR_BEFORE_SEND; + return responseCode_; +} + +int HTTPRequest::getAllResponseHeaders(u32 headerAddrPtr, u32 headerSizePtr) { + // FIXME: Will sceHttpGetAllHeader returns an error if the request was not sent yet? + //if (progress_.progress == 0.0f) + // return SCE_HTTP_ERROR_BEFORE_SEND; + + const char* const delim = "\r\n"; + std::ostringstream imploded; + std::copy(responseHeaders_.begin(), responseHeaders_.end(), std::ostream_iterator(imploded, delim)); + const std::string& s = httpLine_ + delim + imploded.str(); + u32 sz = (u32)s.size(); + + auto headerAddr = PSPPointer::Create(headerAddrPtr); + auto headerSize = PSPPointer::Create(headerSizePtr); + // Resize internal header buffer (should probably be part of network memory pool?) + // FIXME: Do we still need to provides a valid address for the game even when header size is 0 ? + if (headerSize_ != sz && sz > 0) { + if (Memory::IsValidAddress(headerAddr_)) { + userMemory.Free(headerAddr_); + } + headerAddr_ = userMemory.Alloc(sz, false, "sceHttp response headers"); + headerSize_ = sz; + } + + u8* header = Memory::GetPointerWrite(headerAddr_); + DEBUG_LOG(SCENET, "headerAddr: %08x => %08x", headerAddr.IsValid() ? *headerAddr : 0, headerAddr_); + DEBUG_LOG(SCENET, "headerSize: %d => %d", headerSize.IsValid() ? *headerSize : 0, sz); + if (!header && sz > 0) { + ERROR_LOG(SCENET, "Failed to allocate internal header buffer."); + //*headerSize = 0; + //*headerAddr = 0; + return SCE_HTTP_ERROR_OUT_OF_MEMORY; // SCE_HTTP_ERROR_TOO_LARGE_RESPONSE_HEADER + } + + if (sz > 0) { + memcpy(header, s.c_str(), sz); + NotifyMemInfo(MemBlockFlags::WRITE, headerAddr_, sz, "HttpGetAllHeader"); + } + + // Set the output + if (headerSize.IsValid()) { + *headerSize = sz; + headerSize.NotifyWrite("HttpGetAllHeader"); + } + + if (headerAddr.IsValid()) { + *headerAddr = headerAddr_; + headerAddr.NotifyWrite("HttpGetAllHeader"); + } + + DEBUG_LOG(SCENET, "Headers: %s", s.c_str()); + return 0; +} + +int HTTPRequest::readData(u32 destDataPtr, u32 size) { + // FIXME: Will sceHttpReadData returns an error if the request was not sent yet? + //if (progress_.progress == 0.0f) + // return SCE_HTTP_ERROR_BEFORE_SEND; + u32 sz = std::min(size, (u32)responseContent_.size()); + if (sz > 0) { + Memory::MemcpyUnchecked(destDataPtr, responseContent_.c_str(), sz); + NotifyMemInfo(MemBlockFlags::WRITE, destDataPtr, sz, "HttpReadData"); + responseContent_.erase(0, sz); + } + return sz; +} + +int HTTPRequest::sendRequest(u32 postDataPtr, u32 postDataSize) { + // Initialize Connection + client.SetDataTimeout(getRecvTimeout() / 1000000.0); + // Initialize Headers + if (getHttpVer() == SCE_HTTP_VERSION_1_0) + client.SetHttpVersion("1.0"); + else + client.SetHttpVersion("1.1"); + client.SetUserAgent(getUserAgent()); + if (postDataSize > 0) + requestHeaders_["Content-Length"] = std::to_string(postDataSize); + const std::string delimiter = "\r\n"; + const std::string extraHeaders = std::accumulate(requestHeaders_.begin(), requestHeaders_.end(), std::string(), + [delimiter](const std::string& s, const std::pair& p) { + return s + p.first + ": " + p.second + delimiter; + }); + + // TODO: Do this on a separate thread, since this may blocks "Emu" thread here + // Try to resolve first + // Note: LittleBigPlanet onlu passed the path (ie. /LITTLEBIGPLANETPSP_XML/login?) during sceHttpCreateRequest without the host domain, thus will need to be construced into a valid URI using the data from sceHttpCreateConnection upon validating/parsing the URL. + std::string fullURL = url; + if (startsWithNoCase(url, "/")) { + fullURL = scheme + "://" + hostString + ":" + std::to_string(port) + fullURL; + } + + Url fileUrl(fullURL); + if (!fileUrl.Valid()) { + return SCE_HTTP_ERROR_INVALID_URL; + } + if (!client.Resolve(fileUrl.Host().c_str(), fileUrl.Port())) { + ERROR_LOG(IO, "Failed resolving %s", fileUrl.ToString().c_str()); + return -1; + } + + // Establish Connection + if (!client.Connect(getResolveRetryCount(), getConnectTimeout() / 1000000.0, &cancelled_)) { + ERROR_LOG(SCENET, "Failed connecting to server or cancelled."); + return -1; // SCE_HTTP_ERROR_ABORTED + } + if (cancelled_) { + return SCE_HTTP_ERROR_ABORTED; + } + + // Send the Request + std::string methodstr = "GET"; + switch (method) + { + case PSP_HTTP_METHOD_POST: + methodstr = "POST"; + break; + case PSP_HTTP_METHOD_HEAD: + methodstr = "HEAD"; + break; + default: + break; + } + net::Buffer buffer_; + net::RequestProgress progress_(&cancelled_); + http::RequestParams req(fileUrl.Resource(), "*/*"); + const char* postData = Memory::GetCharPointer(postDataPtr); + if (postDataSize > 0) + NotifyMemInfo(MemBlockFlags::READ, postDataPtr, postDataSize, "HttpSendRequest"); + int err = client.SendRequestWithData(methodstr.c_str(), req, std::string(postData ? postData : "", postData ? postDataSize : 0), extraHeaders.c_str(), &progress_); + if (cancelled_) { + return SCE_HTTP_ERROR_ABORTED; + } + if (err < 0) { + return err; // SCE_HTTP_ERROR_BAD_RESPONSE; + } + + // Retrieve Response's Status Code (and Headers too?) + responseCode_ = client.ReadResponseHeaders(&buffer_, responseHeaders_, &progress_, &httpLine_); + if (cancelled_) { + return SCE_HTTP_ERROR_ABORTED; + } + + // TODO: Read response entity within readData() in smaller chunk(based on size arg of sceHttpReadData) instead of the whole content at once here + net::Buffer entity_; + int res = client.ReadResponseEntity(&buffer_, responseHeaders_, &entity_, &progress_); + if (res != 0) { + ERROR_LOG(SCENET, "Unable to read HTTP response entity: %d", res); + } + entity_.TakeAll(&responseContent_); + if (cancelled_) { + return SCE_HTTP_ERROR_ABORTED; + } + + return 0; +} + +static void __HttpNotifyLifecycle(CoreLifecycle stage) { + if (stage == CoreLifecycle::STOPPING) { + for (const auto& it : httpObjects) { + if (it->className() == name_HTTPRequest) + (static_cast(it.get()))->abortRequest(); + } + } +} + +static void __HttpRequestStop() { + // This can happen from a separate thread. + std::lock_guard guard(httpLock); + for (const auto& it : httpObjects) { + if (it->className() == name_HTTPRequest) + (static_cast(it.get()))->abortRequest(); + } +} + +void __HttpInit() { + Core_ListenLifecycle(&__HttpNotifyLifecycle); + Core_ListenStopRequest(&__HttpRequestStop); +} + +void __HttpShutdown() { + std::lock_guard guard(httpLock); + httpInited = false; + httpsInited = false; + httpCacheInited = false; + httpObjects.clear(); +} + +// id: ID of the template or connection +int sceHttpSetResolveRetry(int id, int retryCount) { + WARN_LOG(SCENET, "UNTESTED sceHttpSetResolveRetry(%d, %d)", id, retryCount); + if (id <= 0 || id > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + const auto& conn = httpObjects[id - 1LL]; + if (!(conn->className() == name_HTTPTemplate || conn->className() == name_HTTPConnection)) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id (%s)", conn->className()); + + conn->setResolveRetry(retryCount); + return 0; +} + +static int sceHttpInit(int poolSize) { + WARN_LOG(SCENET, "UNTESTED sceHttpInit(%i) at %08x", poolSize, currentMIPS->pc); + if (httpInited) + return hleLogError(SCENET, SCE_HTTP_ERROR_ALREADY_INITED, "http already inited"); + + std::lock_guard guard(httpLock); + httpObjects.clear(); + // Reserve at least 1 element to prevent ::begin() from returning null when no element has been added yet + httpObjects.reserve(1); + httpInited = true; return 0; } static int sceHttpEnd() { - ERROR_LOG(SCENET, "UNIMPL sceHttpEnd()"); + WARN_LOG(SCENET, "UNTESTED sceHttpEnd()"); + std::lock_guard guard(httpLock); + httpObjects.clear(); + httpInited = false; return 0; } static int sceHttpInitCache(int size) { ERROR_LOG(SCENET, "UNIMPL sceHttpInitCache(%d)", size); + httpCacheInited = true; return 0; } static int sceHttpEndCache() { ERROR_LOG(SCENET, "UNIMPL sceHttpEndCache()"); + httpCacheInited = false; return 0; } -static int sceHttpEnableCache(int id) { - ERROR_LOG(SCENET, "UNIMPL sceHttpEnableCache(%d)", id); +static int sceHttpEnableCache(int templateID) { + ERROR_LOG(SCENET, "UNIMPL sceHttpEnableCache(%d)", templateID); return 0; } -static int sceHttpDisableCache(int id) { - ERROR_LOG(SCENET, "UNIMPL sceHttpDisableCache(%d)", id); +// FIXME: Can be TemplateID or ConnectionID ? Megaman PoweredUp seems to use both id on sceHttpDisableCache +static int sceHttpDisableCache(int templateID) { + ERROR_LOG(SCENET, "UNIMPL sceHttpDisableCache(%d)", templateID); return 0; } @@ -86,42 +386,127 @@ static u32 sceHttpGetProxy(u32 id, u32 activateFlagPtr, u32 modePtr, u32 proxyHo } static int sceHttpGetStatusCode(int requestID, u32 statusCodePtr) { - ERROR_LOG(SCENET, "UNIMPL sceHttpGetStatusCode(%d, %x)", requestID, statusCodePtr); + WARN_LOG(SCENET, "UNTESTED sceHttpGetStatusCode(%d, %x)", requestID, statusCodePtr); + if (requestID <= 0 || requestID > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (!Memory::IsValidRange(statusCodePtr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); //SCE_HTTP_ERROR_INVALID_VALUE; + + const auto& req = (HTTPRequest*)httpObjects[requestID - 1LL].get(); + // FIXME: According to JPCSP, try to connect the request first + //req->connect(); + int status = req->getStatusCode(); + + DEBUG_LOG(SCENET, "StatusCode = %d (in) => %d (out)", Memory::ReadUnchecked_U32(statusCodePtr), status); + Memory::WriteUnchecked_U32(status, statusCodePtr); + NotifyMemInfo(MemBlockFlags::WRITE, statusCodePtr, 4, "HttpGetStatusCode"); return 0; } +// Games will repeatedly called sceHttpReadData until it returns (the size read into the data buffer) 0 +// FIXME: sceHttpReadData seems to be blocking current thread, since hleDelayResult can make Download progressbar to moves progressively instead of instantly jump to 100% static int sceHttpReadData(int requestID, u32 dataPtr, u32 dataSize) { - ERROR_LOG(SCENET, "UNIMPL sceHttpReadData(%d, %x, %x)", requestID, dataPtr, dataSize); - return 0; + WARN_LOG(SCENET, "UNTESTED sceHttpReadData(%d, %x, %d)", requestID, dataPtr, dataSize); + if (requestID <= 0 || requestID > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (!Memory::IsValidRange(dataPtr, dataSize)) + return hleLogError(SCENET, -1, "invalid arg"); // SCE_HTTP_ERROR_INVALID_VALUE + + const auto& req = (HTTPRequest*)httpObjects[requestID - 1LL].get(); + // FIXME: According to JPCSP, try to connect the request first + //req->connect(); + + DEBUG_LOG(SCENET, "Entity remaining / size = %d / %d", req->getResponseRemainingContentLength(), req->getResponseContentLength()); + //if (req->getResponseContentLength()) == 0) + // return hleLogError(SCENET, SCE_HTTP_ERROR_NO_CONTENT_LENGTH, "no content length"); + int retval = req->readData(dataPtr, dataSize); + + if (retval > 0) { + u8* data = (u8*)Memory::GetPointerUnchecked(dataPtr); + std::string datahex; + DataToHexString(10, 0, data, retval, &datahex); + DEBUG_LOG(SCENET, "Data Dump (%d bytes):\n%s", retval, datahex.c_str()); + } + + // Faking latency to slow down download progressbar, since we currently downloading the full content at once instead of in chunk per sceHttpReadData's dataSize + return hleDelayResult(hleLogDebug(SCENET, retval), "fake read data latency", 5000); } +// FIXME: JPCSP didn't do anything other than appending the data into internal buffer, does sceHttpSendRequest can be called multiple times before using sceHttpGetStatusCode or sceHttpReadData? any game do this? static int sceHttpSendRequest(int requestID, u32 dataPtr, u32 dataSize) { - ERROR_LOG(SCENET, "UNIMPL sceHttpSendRequest(%d, %x, %x)", requestID, dataPtr, dataSize); - return 0; + WARN_LOG(SCENET, "UNTESTED sceHttpSendRequest(%d, %x, %x)", requestID, dataPtr, dataSize); + if (!httpInited) + return hleLogError(SCENET, SCE_HTTP_ERROR_BEFORE_INIT, "http not initialized yet"); + + if (requestID <= 0 || requestID > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (dataSize > 0 && !Memory::IsValidRange(dataPtr, dataSize)) + return hleLogError(SCENET, -1, "invalid arg"); // SCE_HTTP_ERROR_INVALID_VALUE + + const auto& req = (HTTPRequest*)httpObjects[requestID - 1LL].get(); + // Internally try to connect, and get response headers (at least the status code?) + int retval = req->sendRequest(dataPtr, dataSize); + return hleLogDebug(SCENET, retval); } static int sceHttpDeleteRequest(int requestID) { - ERROR_LOG(SCENET, "UNIMPL sceHttpDeleteRequest(%d)", requestID); + WARN_LOG(SCENET, "UNTESTED sceHttpDeleteRequest(%d)", requestID); + std::lock_guard guard(httpLock); + if (requestID <= 0 || requestID > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (httpObjects[requestID - 1LL]->className() != name_HTTPRequest) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + httpObjects.erase(httpObjects.begin() + requestID - 1); return 0; } +// id: ID of the template, connection or request static int sceHttpDeleteHeader(int id, const char *name) { - ERROR_LOG(SCENET, "UNIMPL sceHttpDeleteHeader(%d, %s)", id, name); - return 0; + WARN_LOG(SCENET, "UNTESTED sceHttpDeleteHeader(%d, %s)", id, safe_string(name)); + if (id <= 0 || id > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + const auto& req = (HTTPRequest*)httpObjects[id - 1LL].get(); + return req->removeRequestHeader(name); } static int sceHttpDeleteConnection(int connectionID) { - ERROR_LOG(SCENET, "UNIMPL sceHttpDisableCache(%d)", connectionID); + WARN_LOG(SCENET, "UNTESTED sceHttpDisableCache(%d)", connectionID); + std::lock_guard guard(httpLock); + if (connectionID <= 0 || connectionID > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (httpObjects[connectionID - 1LL]->className() != name_HTTPConnection) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + httpObjects.erase(httpObjects.begin() + connectionID - 1); return 0; } +// id: ID of the template, connection or request static int sceHttpSetConnectTimeOut(int id, u32 timeout) { - ERROR_LOG(SCENET, "UNIMPL sceHttpSetConnectTimeout(%d, %d)", id, timeout); + WARN_LOG(SCENET, "UNTESTED sceHttpSetConnectTimeout(%d, %d)", id, timeout); + if (id <= 0 || id > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + auto& conn = httpObjects[id - 1LL]; + conn->setConnectTimeout(timeout); return 0; } +// id: ID of the template, connection or request static int sceHttpSetSendTimeOut(int id, u32 timeout) { ERROR_LOG(SCENET, "UNIMPL sceHttpSetSendTimeout(%d, %d)", id, timeout); + if (id <= 0 || id > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + auto& conn = httpObjects[id - 1LL]; + conn->setSendTimeout(timeout); return 0; } @@ -130,64 +515,125 @@ static u32 sceHttpSetProxy(u32 id, u32 activateFlagPtr, u32 mode, u32 newProxyHo return 0; } +// id: ID of the template or connection static int sceHttpEnableCookie(int id) { ERROR_LOG(SCENET, "UNIMPL sceHttpEnableCookie(%d)", id); return 0; } +// id: ID of the template or connection static int sceHttpEnableKeepAlive(int id) { ERROR_LOG(SCENET, "UNIMPL sceHttpEnableKeepAlive(%d)", id); return 0; } +// id: ID of the template or connection static int sceHttpDisableCookie(int id) { ERROR_LOG(SCENET, "UNIMPL sceHttpDisableCookie(%d)", id); return 0; } +// id: ID of the template or connection static int sceHttpDisableKeepAlive(int id) { ERROR_LOG(SCENET, "UNIMPL sceHttpDisableKeepAlive(%d)", id); return 0; } static int sceHttpsInit(int unknown1, int unknown2, int unknown3, int unknown4) { - ERROR_LOG(SCENET, "UNIMPL sceHttpsInit(%d, %d, %d, %d)", unknown1, unknown2, unknown3, unknown4); + ERROR_LOG(SCENET, "UNIMPL sceHttpsInit(%d, %d, %d, %x)", unknown1, unknown2, unknown3, unknown4); + httpsInited = true; + return 0; +} + +static int sceHttpsInitWithPath(int unknown1, int unknown2, int unknown3) { + ERROR_LOG(SCENET, "UNIMPL sceHttpsInitWithPath(%d, %d, %d)", unknown1, unknown2, unknown3); + httpsInited = true; return 0; } static int sceHttpsEnd() { ERROR_LOG(SCENET, "UNIMPL sceHttpsEnd()"); + httpsInited = false; + return 0; +} + +static int sceHttpsDisableOption(int id) { + ERROR_LOG(SCENET, "UNIMPL sceHttpsDisableOption(%d)", id); return 0; } // Parameter "method" should be one of PSPHttpMethod's listed entries static int sceHttpCreateRequest(int connectionID, int method, const char *path, u64 contentLength) { - ERROR_LOG(SCENET, "UNIMPL sceHttpCreateRequest(%d, %d, %s, %llx)", connectionID, method, path, contentLength); - return 0; + WARN_LOG(SCENET, "UNTESTED sceHttpCreateRequest(%d, %d, %s, %llx)", connectionID, method, safe_string(path), contentLength); + std::lock_guard guard(httpLock); + if (connectionID <= 0 || connectionID > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (httpObjects[connectionID - 1LL]->className() != name_HTTPConnection) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (method < PSPHttpMethod::PSP_HTTP_METHOD_GET || method > PSPHttpMethod::PSP_HTTP_METHOD_HEAD) + return hleLogError(SCENET, SCE_HTTP_ERROR_UNKNOWN_METHOD, "unknown method"); + + httpObjects.emplace_back(std::make_shared(connectionID, method, path? path:"", contentLength)); + int retid = (int)httpObjects.size(); + return hleLogSuccessI(SCENET, retid); } -static int sceHttpCreateConnection(int templateID, const char *hostString, const char *unknown1, u32 port, int unknown2) { - ERROR_LOG(SCENET, "UNIMPL sceHttpCreateConnection(%d, %s, %s, %d, %d)", templateID, hostString, unknown1, port, unknown2); - return 0; +// FIXME: port type is probably u16 +static int sceHttpCreateConnection(int templateID, const char *hostString, const char *scheme, u32 port, int enableKeepalive) { + WARN_LOG(SCENET, "UNTESTED sceHttpCreateConnection(%d, %s, %s, %d, %d)", templateID, safe_string(hostString), safe_string(scheme), port, enableKeepalive); + std::lock_guard guard(httpLock); + if (templateID <= 0 || templateID > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (httpObjects[templateID - 1LL]->className() != name_HTTPTemplate) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + httpObjects.emplace_back(std::make_shared(templateID, hostString ? hostString : "", scheme ? scheme : "", port, enableKeepalive)); + int retid = (int)httpObjects.size(); + return hleLogSuccessI(SCENET, retid); } static int sceHttpGetNetworkErrno(int request, u32 errNumPtr) { ERROR_LOG(SCENET, "UNIMPL sceHttpGetNetworkErrno(%d, %x)", request, errNumPtr); + if (Memory::IsValidRange(errNumPtr, 4)) { + INFO_LOG(SCENET, "Input errNum = %d", Memory::ReadUnchecked_U32(errNumPtr)); + Memory::WriteUnchecked_U32(0, errNumPtr); // dummy error code 0 (no error?) + NotifyMemInfo(MemBlockFlags::WRITE, errNumPtr, 4, "HttpGetNetworkErrno"); + } return 0; } +// id: ID of the template, connection or request static int sceHttpAddExtraHeader(int id, const char *name, const char *value, int unknown) { - ERROR_LOG(SCENET, "UNIMPL sceHttpAddExtraHeader(%d, %s, %s, %d)", id, name, value, unknown); - return 0; + WARN_LOG(SCENET, "UNTESTED sceHttpAddExtraHeader(%d, %s, %s, %d)", id, safe_string(name), safe_string(value), unknown); + if (id <= 0 || id > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + const auto& req = (HTTPRequest*)httpObjects[id - 1LL].get(); + return req->addRequestHeader(name, value, unknown); } static int sceHttpAbortRequest(int requestID) { - ERROR_LOG(SCENET, "UNIMPL sceHttpAbortRequest(%d)", requestID); - return 0; + WARN_LOG(SCENET, "UNTESTED sceHttpAbortRequest(%d)", requestID); + if (requestID <= 0 || requestID > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + const auto& req = (HTTPRequest*)httpObjects[requestID - 1LL].get(); + return req->abortRequest(); } static int sceHttpDeleteTemplate(int templateID) { - ERROR_LOG(SCENET, "UNIMPL sceHttpDeleteTemplate(%d)", templateID); + WARN_LOG(SCENET, "UNTESTED sceHttpDeleteTemplate(%d)", templateID); + std::lock_guard guard(httpLock); + if (templateID <= 0 || templateID > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (httpObjects[templateID - 1LL]->className() != name_HTTPTemplate) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + httpObjects.erase(httpObjects.begin() + templateID - 1); return 0; } @@ -196,8 +642,35 @@ static int sceHttpSetMallocFunction(u32 mallocFuncPtr, u32 freeFuncPtr, u32 real return 0; } +// id: ID of the template or connection static int sceHttpSetResolveTimeOut(int id, u32 timeout) { ERROR_LOG(SCENET, "UNIMPL sceHttpSetResolveTimeOut(%d, %d)", id, timeout); + if (id <= 0 || id > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + const auto& conn = httpObjects[id - 1LL]; + if (!(conn->className() == name_HTTPTemplate || conn->className() == name_HTTPConnection)) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id (%s)", conn->className()); + + conn->setResolveTimeout(timeout); + return 0; +} + +//typedef int(* SceHttpsCallback) (unsigned int verifyEsrr, void *const sslCert[], int certNum, void *userArg) +static int sceHttpsSetSslCallback(int id, u32 callbackFuncPtr, u32 userArgPtr) { + ERROR_LOG(SCENET, "UNIMPL sceHttpsSetSslCallback(%d, %x, %x)", id, callbackFuncPtr, userArgPtr); + return 0; +} + +//typedef int(*SceHttpRedirectCallback) (int request, int statusCode, int* method, const char* location, void* userArg); +static int sceHttpSetRedirectCallback(int requestID, u32 callbackFuncPtr, u32 userArgPtr) { + ERROR_LOG(SCENET, "UNIMPL sceHttpSetRedirectCallback(%d, %x, %x)", requestID, callbackFuncPtr, userArgPtr); + return 0; +} + +//typedef int(*SceHttpAuthInfoCallback) (int request, SceHttpAuthType authType, const char* realm, char* username, char* password, int needEntity, unsigned char** entityBody, unsigned int* entitySize, int* save, void* userArg); +static int sceHttpSetAuthInfoCallback(int id, u32 callbackFuncPtr, u32 userArgPtr) { + ERROR_LOG(SCENET, "UNIMPL sceHttpSetAuthInfoCallback(%d, %x, %x)", id, callbackFuncPtr, userArgPtr); return 0; } @@ -206,23 +679,25 @@ static int sceHttpSetAuthInfoCB(int id, u32 callbackFuncPtr) { return 0; } +// id: ID of the template or connection static int sceHttpEnableRedirect(int id) { ERROR_LOG(SCENET, "UNIMPL sceHttpEnableRedirect(%d)", id); return 0; } -static int sceHttpEnableAuth(int id) { - ERROR_LOG(SCENET, "UNIMPL sceHttpEnableAuth(%d)", id); +static int sceHttpEnableAuth(int templateID) { + ERROR_LOG(SCENET, "UNIMPL sceHttpEnableAuth(%d)", templateID); return 0; } +// id: ID of the template or connection static int sceHttpDisableRedirect(int id) { ERROR_LOG(SCENET, "UNIMPL sceHttpDisableRedirect(%d)", id); return 0; } -static int sceHttpDisableAuth(int id) { - ERROR_LOG(SCENET, "UNIMPL sceHttpDisableAuth(%d)", id); +static int sceHttpDisableAuth(int templateID) { + ERROR_LOG(SCENET, "UNIMPL sceHttpDisableAuth(%d)", templateID); return 0; } @@ -241,42 +716,118 @@ static int sceHttpLoadSystemCookie() { return 0; } -static int sceHttpCreateTemplate(const char *agent, int unknown1, int unknown2) { - ERROR_LOG(SCENET, "UNIMPL sceHttpCreateTemplate(%s, %d, %d)", agent, unknown1, unknown2); - return 0; +// PSP Browser seems to set userAgent to 0 and later set the User-Agent header using sceHttpAddExtraHeader +static int sceHttpCreateTemplate(const char *userAgent, int httpVer, int autoProxyConf) { + WARN_LOG(SCENET, "UNTESTED sceHttpCreateTemplate(%s, %d, %d) at %08x", safe_string(userAgent), httpVer, autoProxyConf, currentMIPS->pc); + // Reporting to find more games to be tested + DEBUG_LOG_REPORT_ONCE(sceHttpCreateTemplate, SCENET, "UNTESTED sceHttpCreateTemplate(%s, %d, %d)", safe_string(userAgent), httpVer, autoProxyConf); + std::lock_guard guard(httpLock); + httpObjects.push_back(std::make_shared(userAgent? userAgent:"", httpVer, autoProxyConf)); + int retid = (int)httpObjects.size(); + return hleLogSuccessI(SCENET, retid); } // Parameter "method" should be one of PSPHttpMethod's listed entries static int sceHttpCreateRequestWithURL(int connectionID, int method, const char *url, u64 contentLength) { - ERROR_LOG(SCENET, "UNIMPL sceHttpCreateRequestWithURL(%d, %d, %s, %llx)", connectionID, method, url, contentLength); - return 0; + WARN_LOG(SCENET, "UNTESTED sceHttpCreateRequestWithURL(%d, %d, %s, %llx)", connectionID, method, safe_string(url), contentLength); + std::lock_guard guard(httpLock); + if (connectionID <= 0 || connectionID > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (httpObjects[connectionID - 1LL]->className() != name_HTTPConnection) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (method < PSPHttpMethod::PSP_HTTP_METHOD_GET || method > PSPHttpMethod::PSP_HTTP_METHOD_HEAD) + return hleLogError(SCENET, SCE_HTTP_ERROR_UNKNOWN_METHOD, "unknown method"); + + Url baseURL(url ? url : ""); + if (!baseURL.Valid()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_URL, "invalid url"); + + httpObjects.emplace_back(std::make_shared(connectionID, method, url? url:"", contentLength)); + int retid = (int)httpObjects.size(); + return hleLogSuccessI(SCENET, retid); } -static int sceHttpCreateConnectionWithURL(int templateID, const char *url, int unknown1) { - ERROR_LOG(SCENET, "UNIMPL sceHttpCreateConnectionWithURL(%d, %s, %d)", templateID, url, unknown1); - return 0; +static int sceHttpCreateConnectionWithURL(int templateID, const char *url, int enableKeepalive) { + WARN_LOG(SCENET, "UNTESTED sceHttpCreateConnectionWithURL(%d, %s, %d)", templateID, safe_string(url), enableKeepalive); + std::lock_guard guard(httpLock); + if (templateID <= 0 || templateID > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (httpObjects[templateID - 1LL]->className() != name_HTTPTemplate) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + Url baseURL(url? url: ""); + if (!baseURL.Valid()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_URL, "invalid url"); + + httpObjects.emplace_back(std::make_shared(templateID, baseURL.Host().c_str(), baseURL.Protocol().c_str(), baseURL.Port(), enableKeepalive)); + int retid = (int)httpObjects.size(); + return hleLogSuccessI(SCENET, retid); } +// id: ID of the template or connection static int sceHttpSetRecvTimeOut(int id, u32 timeout) { - ERROR_LOG(SCENET, "UNIMPL sceHttpSetRecvTimeOut(%d, %x)", id, timeout); + WARN_LOG(SCENET, "UNTESTED sceHttpSetRecvTimeOut(%d, %d)", id, timeout); + if (id <= 0 || id > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + const auto& conn = httpObjects[id - 1LL]; + if (!(conn->className() == name_HTTPTemplate || conn->className() == name_HTTPConnection)) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id (%s)", conn->className()); + + conn->setRecvTimeout(timeout); return 0; } -static int sceHttpGetAllHeader(int request, u32 headerPtrToPtr, u32 headerSize) { - ERROR_LOG(SCENET, "UNIMPL sceHttpGetAllHeader(%d, %x, %x)", request, headerPtrToPtr, headerSize); - return 0; +// FIXME: Headers should includes the "HTTP/MajorVer.MinorVer StatusCode Comment" line? so PSP Browser can parse it using sceParseHttpStatusLine +// Note: Megaman PoweredUp seems to have an invalid address stored at the headerAddrPtr location, may be the game expecting us (network library) to give them a valid header address? +static int sceHttpGetAllHeader(int requestID, u32 headerAddrPtr, u32 headerSizePtr) { + WARN_LOG(SCENET, "UNTESTED sceHttpGetAllHeader(%d, %x, %x)", requestID, headerAddrPtr, headerSizePtr); + if (requestID <= 0 || requestID > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (!Memory::IsValidRange(headerAddrPtr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); //SCE_HTTP_ERROR_INVALID_VALUE; + + if (!Memory::IsValidRange(headerSizePtr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); //SCE_HTTP_ERROR_INVALID_VALUE; + + const auto& req = (HTTPRequest*)httpObjects[requestID - 1LL].get(); + // FIXME: According to JPCSP, try to connect the request first + //req->connect(); + int retval = req->getAllResponseHeaders(headerAddrPtr, headerSizePtr); + return hleLogDebug(SCENET, retval); } -static int sceHttpGetContentLength(int requestID, u64 contentLengthPtr) { - ERROR_LOG(SCENET, "UNIMPL sceHttpGetContentLength(%d, %llx)", requestID, contentLengthPtr); +// FIXME: contentLength is SceULong64 but this contentLengthPtr argument should be a 32bit pointer instead of 64bit, right? +static int sceHttpGetContentLength(int requestID, u32 contentLengthPtr) { + WARN_LOG(SCENET, "UNTESTED sceHttpGetContentLength(%d, %x)", requestID, contentLengthPtr); + if (requestID <= 0 || requestID > httpObjects.size()) + return hleLogError(SCENET, SCE_HTTP_ERROR_INVALID_ID, "invalid id"); + + if (!Memory::IsValidRange(contentLengthPtr, 8)) + return hleLogError(SCENET, -1, "invalid arg"); //SCE_HTTP_ERROR_INVALID_VALUE; + + const auto& req = (HTTPRequest*)httpObjects[requestID - 1LL].get(); + // FIXME: According to JPCSP, try to connect the request first + //req->connect(); + int len = req->getResponseContentLength(); + if (len < 0) + return hleLogError(SCENET, SCE_HTTP_ERROR_NO_CONTENT_LENGTH, "no content length"); + + DEBUG_LOG(SCENET, "ContentLength = %lld (in) => %lld (out)", Memory::Read_U64(contentLengthPtr), (u64)len); + Memory::Write_U64((u64)len, contentLengthPtr); + NotifyMemInfo(MemBlockFlags::WRITE, contentLengthPtr, 8, "HttpGetContentLength"); return 0; } /* -* 0x62411801 sceSircsInit +0x62411801 sceSircsInit 0x19155a2f sceSircsEnd 0x71eef62d sceSircsSend - */ +*/ const HLEFunction sceHttp[] = { {0XAB1ABE07, &WrapI_I, "sceHttpInit", 'i', "i" }, {0XD1C8945E, &WrapI_V, "sceHttpEnd", 'i', "" }, @@ -322,15 +873,15 @@ const HLEFunction sceHttp[] = { {0XCDF8ECB9, &WrapI_ICI, "sceHttpCreateConnectionWithURL", 'i', "isi" }, {0X1F0FC3E3, &WrapI_IU, "sceHttpSetRecvTimeOut", 'i', "ix" }, {0XDB266CCF, &WrapI_IUU, "sceHttpGetAllHeader", 'i', "ixx" }, - {0X0282A3BD, &WrapI_IU64, "sceHttpGetContentLength", 'i', "iX" }, + {0X0282A3BD, &WrapI_IU, "sceHttpGetContentLength", 'i', "ix" }, {0X7774BF4C, nullptr, "sceHttpAddCookie", '?', "" }, - {0X68AB0F86, nullptr, "sceHttpsInitWithPath", '?', "" }, - {0XB3FAF831, nullptr, "sceHttpsDisableOption", '?', "" }, + {0X68AB0F86, &WrapI_III, "sceHttpsInitWithPath", 'i', "iii" }, + {0XB3FAF831, &WrapI_I, "sceHttpsDisableOption", 'i', "i" }, {0X2255551E, nullptr, "sceHttpGetNetworkPspError", '?', "" }, {0XAB1540D5, nullptr, "sceHttpsGetSslError", '?', "" }, - {0XA4496DE5, nullptr, "sceHttpSetRedirectCallback", '?', "" }, - {0X267618F4, nullptr, "sceHttpSetAuthInfoCallback", '?', "" }, - {0X569A1481, nullptr, "sceHttpsSetSslCallback", '?', "" }, + {0XA4496DE5, &WrapI_IUU, "sceHttpSetRedirectCallback", 'i', "ixx" }, + {0X267618F4, &WrapI_IUU, "sceHttpSetAuthInfoCallback", 'i', "ixx" }, + {0X569A1481, &WrapI_IUU, "sceHttpsSetSslCallback", 'i', "ixx" }, {0XBAC31BF1, nullptr, "sceHttpsEnableOption", '?', "" }, }; diff --git a/Core/HLE/sceHttp.h b/Core/HLE/sceHttp.h index e18f02e36ca9..47101159c898 100644 --- a/Core/HLE/sceHttp.h +++ b/Core/HLE/sceHttp.h @@ -17,6 +17,276 @@ #pragma once -int sceHttpSetResolveRetry(int connectionID, int retryCount); +#include +#include "Common/Net/HTTPClient.h" -void Register_sceHttp(); \ No newline at end of file +// Based on https://docs.vitasdk.org/group__SceHttpUser.html +#define SCE_HTTP_DEFAULT_RESOLVER_TIMEOUT (1 * 1000 * 1000U) +#define SCE_HTTP_DEFAULT_RESOLVER_RETRY (5U) +#define SCE_HTTP_DEFAULT_CONNECT_TIMEOUT (30* 1000 * 1000U) +#define SCE_HTTP_DEFAULT_SEND_TIMEOUT (120* 1000 * 1000U) +#define SCE_HTTP_DEFAULT_RECV_TIMEOUT (120* 1000 * 1000U) +#define SCE_HTTP_DEFAULT_RECV_BLOCK_SIZE (1500U) +#define SCE_HTTP_DEFAULT_RESPONSE_HEADER_MAX (5000U) +#define SCE_HTTP_DEFAULT_REDIRECT_MAX (6U) +#define SCE_HTTP_DEFAULT_TRY_AUTH_MAX (6U) +#define SCE_HTTP_INVALID_ID 0 +#define SCE_HTTP_ENABLE (1) +#define SCE_HTTP_DISABLE (0) +#define SCE_HTTP_USERNAME_MAX_SIZE 256 +#define SCE_HTTP_PASSWORD_MAX_SIZE 256 + +// If http isn't loaded (seems unlikely), most functions should return SCE_KERNEL_ERROR_LIBRARY_NOTFOUND + +// lib_http specific error codes, based on https://uofw.github.io/uofw/lib__http_8h_source.html, combined with https://github.com/vitasdk/vita-headers/blob/master/include/psp2/net/http.h +enum SceHttpErrorCode { + SCE_HTTP_ERROR_BEFORE_INIT = 0x80431001, + SCE_HTTP_ERROR_NOT_SUPPORTED = 0x80431004, + SCE_HTTP_ERROR_ALREADY_INITED = 0x80431020, + SCE_HTTP_ERROR_BUSY = 0x80431021, + SCE_HTTP_ERROR_OUT_OF_MEMORY = 0x80431022, + SCE_HTTP_ERROR_NOT_FOUND = 0x80431025, + + SCE_HTTP_ERROR_UNKNOWN_SCHEME = 0x80431061, + SCE_HTTP_ERROR_NETWORK = 0x80431063, + SCE_HTTP_ERROR_BAD_RESPONSE = 0x80431064, + SCE_HTTP_ERROR_BEFORE_SEND = 0x80431065, + SCE_HTTP_ERROR_AFTER_SEND = 0x80431066, + SCE_HTTP_ERROR_TIMEOUT = 0x80431068, + SCE_HTTP_ERROR_UNKOWN_AUTH_TYPE = 0x80431069, + SCE_HTTP_ERROR_INVALID_VERSION = 0x8043106A, + SCE_HTTP_ERROR_UNKNOWN_METHOD = 0x8043106B, + SCE_HTTP_ERROR_READ_BY_HEAD_METHOD = 0x8043106F, + SCE_HTTP_ERROR_NOT_IN_COM = 0x80431070, + SCE_HTTP_ERROR_NO_CONTENT_LENGTH = 0x80431071, + SCE_HTTP_ERROR_CHUNK_ENC = 0x80431072, + SCE_HTTP_ERROR_TOO_LARGE_RESPONSE_HEADER = 0x80431073, + SCE_HTTP_ERROR_SSL = 0x80431075, + SCE_HTTP_ERROR_INSUFFICIENT_HEAPSIZE = 0x80431077, + SCE_HTTP_ERROR_BEFORE_COOKIE_LOAD = 0x80431078, + SCE_HTTP_ERROR_ABORTED = 0x80431080, + SCE_HTTP_ERROR_UNKNOWN = 0x80431081, + + SCE_HTTP_ERROR_INVALID_ID = 0x80431100, + SCE_HTTP_ERROR_OUT_OF_SIZE = 0x80431104, + SCE_HTTP_ERROR_INVALID_VALUE = 0x804311FE, + + SCE_HTTP_ERROR_PARSE_HTTP_NOT_FOUND = 0x80432025, + SCE_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE = 0x80432060, + SCE_HTTP_ERROR_PARSE_HTTP_INVALID_VALUE = 0x804321FE, + + SCE_HTTP_ERROR_INVALID_URL = 0x80433060, + + SCE_HTTP_ERROR_RESOLVER_EPACKET = 0x80436001, + SCE_HTTP_ERROR_RESOLVER_ENODNS = 0x80436002, + SCE_HTTP_ERROR_RESOLVER_ETIMEDOUT = 0x80436003, + SCE_HTTP_ERROR_RESOLVER_ENOSUPPORT = 0x80436004, + SCE_HTTP_ERROR_RESOLVER_EFORMAT = 0x80436005, + SCE_HTTP_ERROR_RESOLVER_ESERVERFAILURE = 0x80436006, + SCE_HTTP_ERROR_RESOLVER_ENOHOST = 0x80436007, + SCE_HTTP_ERROR_RESOLVER_ENOTIMPLEMENTED = 0x80436008, + SCE_HTTP_ERROR_RESOLVER_ESERVERREFUSED = 0x80436009, + SCE_HTTP_ERROR_RESOLVER_ENORECORD = 0x8043600A +}; + +// lib_https specific error codes, based on https://uofw.github.io/uofw/lib__https_8h_source.html, combined with https://github.com/vitasdk/vita-headers/blob/master/include/psp2/net/http.h +enum SceHttpsErrorCode { + SCE_HTTPS_ERROR_OUT_OF_MEMORY = 0x80435022, + SCE_HTTPS_ERROR_CERT = 0x80435060, + SCE_HTTPS_ERROR_HANDSHAKE = 0x80435061, + SCE_HTTPS_ERROR_IO = 0x80435062, + SCE_HTTPS_ERROR_INTERNAL = 0x80435063, + SCE_HTTPS_ERROR_PROXY = 0x80435064 +}; + +// Could come in handy someday if we ever implement sceHttp* for real. +enum PSPHttpMethod { + PSP_HTTP_METHOD_GET, + PSP_HTTP_METHOD_POST, + PSP_HTTP_METHOD_HEAD +}; + +// Based on https://github.com/vitasdk/vita-headers/blob/master/include/psp2/net/http.h +enum SceHttpStatusCode { + SCE_HTTP_STATUS_CODE_CONTINUE = 100, + SCE_HTTP_STATUS_CODE_SWITCHING_PROTOCOLS = 101, + SCE_HTTP_STATUS_CODE_PROCESSING = 102, + SCE_HTTP_STATUS_CODE_OK = 200, + SCE_HTTP_STATUS_CODE_CREATED = 201, + SCE_HTTP_STATUS_CODE_ACCEPTED = 202, + SCE_HTTP_STATUS_CODE_NON_AUTHORITATIVE_INFORMATION = 203, + SCE_HTTP_STATUS_CODE_NO_CONTENT = 204, + SCE_HTTP_STATUS_CODE_RESET_CONTENT = 205, + SCE_HTTP_STATUS_CODE_PARTIAL_CONTENT = 206, + SCE_HTTP_STATUS_CODE_MULTI_STATUS = 207, + SCE_HTTP_STATUS_CODE_MULTIPLE_CHOICES = 300, + SCE_HTTP_STATUS_CODE_MOVED_PERMANENTLY = 301, + SCE_HTTP_STATUS_CODE_FOUND = 302, + SCE_HTTP_STATUS_CODE_SEE_OTHER = 303, + SCE_HTTP_STATUS_CODE_NOT_MODIFIED = 304, + SCE_HTTP_STATUS_CODE_USE_PROXY = 305, + SCE_HTTP_STATUS_CODE_TEMPORARY_REDIRECT = 307, + SCE_HTTP_STATUS_CODE_BAD_REQUEST = 400, + SCE_HTTP_STATUS_CODE_UNAUTHORIZED = 401, + SCE_HTTP_STATUS_CODE_PAYMENT_REQUIRED = 402, + SCE_HTTP_STATUS_CODE_FORBIDDDEN = 403, + SCE_HTTP_STATUS_CODE_NOT_FOUND = 404, + SCE_HTTP_STATUS_CODE_METHOD_NOT_ALLOWED = 405, + SCE_HTTP_STATUS_CODE_NOT_ACCEPTABLE = 406, + SCE_HTTP_STATUS_CODE_PROXY_AUTHENTICATION_REQUIRED = 407, + SCE_HTTP_STATUS_CODE_REQUEST_TIME_OUT = 408, + SCE_HTTP_STATUS_CODE_CONFLICT = 409, + SCE_HTTP_STATUS_CODE_GONE = 410, + SCE_HTTP_STATUS_CODE_LENGTH_REQUIRED = 411, + SCE_HTTP_STATUS_CODE_PRECONDITION_FAILED = 412, + SCE_HTTP_STATUS_CODE_REQUEST_ENTITY_TOO_LARGE = 413, + SCE_HTTP_STATUS_CODE_REQUEST_URI_TOO_LARGE = 414, + SCE_HTTP_STATUS_CODE_UNSUPPORTED_MEDIA_TYPE = 415, + SCE_HTTP_STATUS_CODE_REQUEST_RANGE_NOT_SATISFIBLE = 416, + SCE_HTTP_STATUS_CODE_EXPECTATION_FAILED = 417, + SCE_HTTP_STATUS_CODE_UNPROCESSABLE_ENTITY = 422, + SCE_HTTP_STATUS_CODE_LOCKED = 423, + SCE_HTTP_STATUS_CODE_FAILED_DEPENDENCY = 424, + SCE_HTTP_STATUS_CODE_UPGRADE_REQUIRED = 426, + SCE_HTTP_STATUS_CODE_INTERNAL_SERVER_ERROR = 500, + SCE_HTTP_STATUS_CODE_NOT_IMPLEMENTED = 501, + SCE_HTTP_STATUS_CODE_BAD_GATEWAY = 502, + SCE_HTTP_STATUS_CODE_SERVICE_UNAVAILABLE = 503, + SCE_HTTP_STATUS_CODE_GATEWAY_TIME_OUT = 504, + SCE_HTTP_STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED = 505, + SCE_HTTP_STATUS_CODE_INSUFFICIENT_STORAGE = 507 +}; + +enum SceHttpVersion { + SCE_HTTP_VERSION_1_0 = 1, + SCE_HTTP_VERSION_1_1 +}; + +enum SceHttpProxyMode { + SCE_HTTP_PROXY_AUTO, + SCE_HTTP_PROXY_MANUAL +}; + +enum SceHttpAddHeaderMode { + SCE_HTTP_HEADER_OVERWRITE, + SCE_HTTP_HEADER_ADD +}; + + +// Just a holder for class names +static const char* name_HTTPTemplate = "HTTPTemplate"; +static const char* name_HTTPConnection = "HTTPConnection"; +static const char* name_HTTPRequest = "HTTPRequest"; + +class HTTPTemplate { +protected: + std::string userAgent; // char userAgent[512]; + SceHttpVersion httpVer = SCE_HTTP_VERSION_1_0; + SceHttpProxyMode autoProxyConf = SCE_HTTP_PROXY_AUTO; + + int useCookie = 0; + int useKeepAlive = 0; + int useCache = 0; + int useAuth = 0; + int useRedirect = 0; + + u32 connectTimeout = SCE_HTTP_DEFAULT_CONNECT_TIMEOUT; + u32 sendTimeout = SCE_HTTP_DEFAULT_SEND_TIMEOUT; + u32 recvTimeout = SCE_HTTP_DEFAULT_RECV_TIMEOUT; + u32 resolveTimeout = SCE_HTTP_DEFAULT_RESOLVER_TIMEOUT; + int resolveRetryCount = SCE_HTTP_DEFAULT_RESOLVER_RETRY; + + std::map requestHeaders_; + +public: + HTTPTemplate() {} + HTTPTemplate(const char* userAgent, int httpVer, int autoProxyConf); + virtual ~HTTPTemplate() = default; + + virtual const char* className() { return name_HTTPTemplate; } // to be more consistent, unlike typeid(v).name() which may varies among different compilers and requires RTTI + + const std::string getUserAgent() { return userAgent; } + int getHttpVer() { return httpVer; } + int getAutoProxyConf() { return autoProxyConf; } + + u32 getConnectTimeout() { return connectTimeout; } + u32 getSendTimeout() { return sendTimeout; } + u32 getRecvTimeout() { return recvTimeout; } + u32 getResolveTimeout() { return resolveTimeout; } + int getResolveRetryCount() { return resolveRetryCount; } + + void setUserAgent(const char* userAgent) { this->userAgent = userAgent ? userAgent : ""; } + void setConnectTimeout(u32 timeout) { this->connectTimeout = timeout; } + void setSendTimeout(u32 timeout) { this->sendTimeout = timeout; } + void setRecvTimeout(u32 timeout) { this->recvTimeout = timeout; } + void setResolveTimeout(u32 timeout) { this->resolveTimeout = timeout; } + void setResolveRetry(u32 retryCount) { this->resolveRetryCount = retryCount; } + + int addRequestHeader(const char* name, const char* value, u32 mode); + int removeRequestHeader(const char* name); +}; + +class HTTPConnection : public HTTPTemplate { +protected: + int templateID = 0; + std::string hostString; + std::string scheme; + u16 port = 80; + int enableKeepalive = 0; + +public: + HTTPConnection() {} + HTTPConnection(int templateID, const char* hostString, const char* scheme, u32 port, int enableKeepalive); + virtual ~HTTPConnection() = default; + + virtual const char* className() override { return name_HTTPConnection; } + + int getTemplateID() { return templateID; } + const std::string getHost() { return hostString; } + const std::string getScheme() { return scheme; } + u16 getPort() { return port; } + int getKeepAlive() { return enableKeepalive; } +}; + +class HTTPRequest : public HTTPConnection { +private: + int connectionID; + int method; + u64 contentLength; + std::string url; + + u32 headerAddr_ = 0; + u32 headerSize_ = 0; + bool cancelled_ = false; + int responseCode_ = -1; + int entityLength_ = -1; + + http::Client client; + //net::RequestProgress progress_(&cancelled_); + std::vector responseHeaders_; + std::string httpLine_; + std::string responseContent_; + +public: + HTTPRequest(int connectionID, int method, const char* url, u64 contentLength); + ~HTTPRequest(); + + virtual const char* className() override { return name_HTTPRequest; } + + void setInternalHeaderAddr(u32 addr) { headerAddr_ = addr; } + int getConnectionID() { return connectionID; } + int getResponseRemainingContentLength() { return (int)responseContent_.size(); } + + int getResponseContentLength(); + int abortRequest(); + int getStatusCode(); + int getAllResponseHeaders(u32 headerAddrPtr, u32 headerSizePtr); + int readData(u32 destDataPtr, u32 size); + int sendRequest(u32 postDataPtr, u32 postDataSize); +}; + + +void __HttpInit(); +void __HttpShutdown(); + +void Register_sceHttp(); diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 40fc161a01b4..d162a3f725ce 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -85,7 +85,9 @@ #include "sceDmac.h" #include "sceMp4.h" #include "sceOpenPSID.h" +#include "sceHttp.h" #include "Core/Util/PPGeDraw.h" +#include "sceHttp.h" /* 17: [MIPS32 R4K 00000000 ]: Loader: Type: 1 Vaddr: 00000000 Filesz: 2856816 Memsz: 2856816 @@ -150,6 +152,7 @@ void __KernelInit() __UsbCamInit(); __UsbMicInit(); __OpenPSIDInit(); + __HttpInit(); SaveState::Init(); // Must be after IO, as it may create a directory Reporting::Init(); @@ -173,6 +176,7 @@ void __KernelShutdown() hleCurrentThreadName = NULL; kernelObjects.Clear(); + __HttpShutdown(); __OpenPSIDShutdown(); __UsbCamShutdown(); __UsbMicShutdown(); diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 2e256fffbbc6..0c52d25f6777 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -15,13 +15,23 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#if defined(_WIN32) +#include "Common/CommonWindows.h" // Needed for std::max/min to works +#endif + #if __linux__ || __APPLE__ || defined(__OpenBSD__) #include #include #include +#include #include #endif +#ifndef MSG_NOSIGNAL +// Default value to 0x00 (do nothing) in systems where it's not supported. +#define MSG_NOSIGNAL 0x00 +#endif + #include "Common/Net/Resolve.h" #include "Common/Data/Text/Parsers.h" @@ -33,6 +43,8 @@ #include "Core/HLE/sceKernelMemory.h" #include "Core/MIPS/MIPS.h" #include "Core/Config.h" +#include "Core/System.h" +#include "Core/ELF/ParamSFO.h" #include "Core/MemMapHelpers.h" #include "Core/Util/PortManager.h" @@ -45,6 +57,7 @@ #include "Core/HLE/sceNetAdhoc.h" #include "Core/HLE/sceNet.h" #include "Core/HLE/sceNp.h" +#include "Core/HLE/sceNp2.h" #include "Core/Reporting.h" #include "Core/Instance.h" @@ -62,14 +75,20 @@ u32 netPoolAddr = 0; u32 netThread1Addr = 0; u32 netThread2Addr = 0; -static struct SceNetMallocStat netMallocStat; +static struct SceNetMallocStat netMallocStat = {}; + +static std::map netResolvers; static std::map apctlHandlers; +std::string defaultNetConfigName = "NetConf"; +std::string defaultNetSSID = "Wifi"; // fake AP/hotspot +int netApctlInfoId = 0; SceNetApctlInfoInternal netApctlInfo; bool netApctlInited; u32 netApctlState; +u32 apctlProdCodeAddr = 0; u32 apctlThreadHackAddr = 0; u32_le apctlThreadCode[3]; SceUID apctlThreadID = 0; @@ -78,9 +97,15 @@ int actionAfterApctlMipsCall; std::recursive_mutex apctlEvtMtx; std::deque apctlEvents; +// TODO: Combine AdhocSocket class and InetSocket class into a single class just like kernelObjects, where Port Offset will be handled internally, and can also be used for future feature (ie. tunneling) +//PSPSocketPool pspSockets; // Keep tracks connection state and nonblocking mode (which is required to simulate blocking mode) +int inetLastErrno = 0; // TODO: since errno can only be read once, we should keep track the value to be used on sceNetInetGetErrno +int inetLastSocket = -1; // A workaround to keep the most recent socket id for sceNetInetSelect, until we have a socket class wrapper + u32 Net_Term(); int NetApctl_Term(); -void NetApctl_InitInfo(); +void NetApctl_InitDefaultInfo(); +void NetApctl_InitInfo(int confId); void AfterApctlMipsCall::DoState(PointerWrap & p) { auto s = p.Section("AfterApctlMipsCall", 1, 1); @@ -171,7 +196,7 @@ void __NetApctlInit() { apctlStateEvent = CoreTiming::RegisterEvent("__ApctlState", __ApctlState); apctlHandlers.clear(); apctlEvents.clear(); - memset(&netApctlInfo, 0, sizeof(netApctlInfo)); + NetApctl_InitDefaultInfo(); } static void __ResetInitNetLib() { @@ -194,6 +219,10 @@ void __NetCallbackInit() { actionAfterApctlMipsCall = __KernelRegisterActionType(AfterApctlMipsCall::Create); } +void __NetResolverInit() { + netResolvers.clear(); +} + void __NetInit() { // Windows: Assuming WSAStartup already called beforehand portOffset = g_Config.iPortOffset; @@ -218,6 +247,7 @@ void __NetInit() { __ResetInitNetLib(); __NetApctlInit(); __NetCallbackInit(); + __NetResolverInit(); } void __NetApctlShutdown() { @@ -229,10 +259,15 @@ void __NetApctlShutdown() { apctlEvents.clear(); } +void __NetResolverShutdown() { + netResolvers.clear(); +} + void __NetShutdown() { // Network Cleanup Net_Term(); + __NetResolverShutdown(); __NetApctlShutdown(); __ResetInitNetLib(); @@ -263,7 +298,7 @@ void netValidateLoopMemory() { // This feels like a dubious proposition, mostly... void __NetDoState(PointerWrap &p) { - auto s = p.Section("sceNet", 1, 5); + auto s = p.Section("sceNet", 1, 6); if (!s) return; @@ -313,6 +348,14 @@ void __NetDoState(PointerWrap &p) { apctlStateEvent = -1; } CoreTiming::RestoreRegisterEvent(apctlStateEvent, "__ApctlState", __ApctlState); + if (s >= 6) { + Do(p, netApctlInfoId); + Do(p, netApctlInfo); + } + else { + netApctlInfoId = 0; + NetApctl_InitDefaultInfo(); + } if (p.mode == p.MODE_READ) { // Let's not change "Inited" value when Loading SaveState in the middle of multiplayer to prevent memory & port leaks @@ -322,6 +365,8 @@ void __NetDoState(PointerWrap &p) { // Discard leftover events apctlEvents.clear(); + // Discard created resolvers for now (since i'm not sure whether the information in the struct is sufficient or not, and we don't support multi-threading yet anyway) + netResolvers.clear(); } } @@ -420,16 +465,31 @@ std::string error2str(u32 errorCode) { return str; } +int getNonBlockingFlag(int sock) { +#ifdef _WIN32 + // Windows can't retrieve nonblocking status + return 0; +#else + int sockflag = fcntl(sock, F_GETFL, O_NONBLOCK); + // Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. + if (sockflag == -1) + sockflag = 0; + return sockflag & O_NONBLOCK; +#endif +} + void __NetApctlCallbacks() { std::lock_guard apctlGuard(apctlEvtMtx); + std::lock_guard npAuthGuard(npAuthEvtMtx); + std::lock_guard npMatching2Guard(npMatching2EvtMtx); hleSkipDeadbeef(); int delayus = 10000; // We are temporarily borrowing APctl thread for NpAuth callbacks for testing to simulate authentication if (!npAuthEvents.empty()) { - auto args = npAuthEvents.front(); + auto& args = npAuthEvents.front(); auto& id = args.data[0]; auto& result = args.data[1]; auto& argAddr = args.data[2]; @@ -447,10 +507,39 @@ void __NetApctlCallbacks() } } + // Temporarily borrowing APctl thread for NpMatching2 callbacks for testing purpose + if (!npMatching2Events.empty()) + { + auto& args = npMatching2Events.front(); + auto& event = args.data[0]; + auto& stat = args.data[1]; + auto& serverIdPtr = args.data[2]; + auto& inStructPtr = args.data[3]; + auto& newStat = args.data[5]; + npMatching2Events.pop_front(); + + delayus = (adhocEventDelay + adhocExtraDelay); + + //int handlerID = id - 1; + for (std::map::iterator it = npMatching2Handlers.begin(); it != npMatching2Handlers.end(); ++it) { + //if (it->first == handlerID) + { + DEBUG_LOG(SCENET, "NpMatching2Callback [HandlerID=%i][EventID=%04x][State=%04x][ArgsPtr=%08x]", it->first, event, stat, it->second.argument); + hleEnqueueCall(it->second.entryPoint, 7, args.data); + } + } + // Per npMatching2 function callback + u32* inStruct = (u32*)Memory::GetPointer(inStructPtr); + if (Memory::IsValidAddress(inStruct[0])) { + DEBUG_LOG(SCENET, "NpMatching2Callback [ServerID=%i][EventID=%04x][State=%04x][FuncAddr=%08x][ArgsPtr=%08x]", *(u32*)Memory::GetPointer(serverIdPtr), event, stat, inStruct[0], inStruct[1]); + hleEnqueueCall(inStruct[0], 7, args.data); + } + } + // How AP works probably like this: Game use sceNetApctl function -> sceNetApctl let the hardware know and do their's thing and have a new State -> Let the game know the resulting State through Event on their handler if (!apctlEvents.empty()) { - auto args = apctlEvents.front(); + auto& args = apctlEvents.front(); auto& oldState = args.data[0]; auto& newState = args.data[1]; auto& event = args.data[2]; @@ -458,14 +547,17 @@ void __NetApctlCallbacks() apctlEvents.pop_front(); // Adjust delay according to current event. - if (event == PSP_NET_APCTL_EVENT_CONNECT_REQUEST || event == PSP_NET_APCTL_EVENT_GET_IP || event == PSP_NET_APCTL_EVENT_SCAN_REQUEST) + if (event == PSP_NET_APCTL_EVENT_CONNECT_REQUEST || event == PSP_NET_APCTL_EVENT_GET_IP || event == PSP_NET_APCTL_EVENT_SCAN_REQUEST || event == PSP_NET_APCTL_EVENT_ESTABLISHED) delayus = adhocEventDelay; else delayus = adhocEventPollDelay; // Do we need to change the oldState? even if there was error? - //if (error == 0) - // oldState = netApctlState; + if (error == 0) + { + //oldState = netApctlState; + netApctlState = newState; + } // Need to make sure netApctlState is updated before calling the callback's mipscall so the game can GetState()/GetInfo() within their handler's subroutine and make use the new State/Info // Should we update NewState & Error accordingly to Event before executing the mipscall ? sceNetApctl* functions might want to set the error value tho, so we probably should leave it untouched, right? @@ -473,35 +565,43 @@ void __NetApctlCallbacks() switch (event) { case PSP_NET_APCTL_EVENT_CONNECT_REQUEST: newState = PSP_NET_APCTL_STATE_JOINING; // Should we set the State to PSP_NET_APCTL_STATE_DISCONNECTED if there was error? - if (error == 0) - apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); // Should we use PSP_NET_APCTL_EVENT_EAP_AUTH if securityType is not NONE? + if (error != 0) + apctlEvents.push_front({ oldState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); + else + apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); // Should we use PSP_NET_APCTL_EVENT_EAP_AUTH if securityType is not NONE? break; case PSP_NET_APCTL_EVENT_ESTABLISHED: newState = PSP_NET_APCTL_STATE_GETTING_IP; - if (error == 0) - apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_GET_IP, 0 }); + // FIXME: Official prx seems to return ERROR 0x80410280 on the next event when using invalid connection profile to Connect? + if (error != 0) + apctlEvents.push_front({ oldState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); + else + apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_GET_IP, 0 }); break; case PSP_NET_APCTL_EVENT_GET_IP: newState = PSP_NET_APCTL_STATE_GOT_IP; - NetApctl_InitInfo(); + NetApctl_InitInfo(netApctlInfoId); break; case PSP_NET_APCTL_EVENT_DISCONNECT_REQUEST: newState = PSP_NET_APCTL_STATE_DISCONNECTED; + delayus = adhocDefaultDelay / 2; // FIXME: Similar to Adhocctl Disconnect, we probably need to change the state within a frame-time (or less to be safer) break; case PSP_NET_APCTL_EVENT_SCAN_REQUEST: newState = PSP_NET_APCTL_STATE_SCANNING; - if (error == 0) - apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_SCAN_COMPLETE, 0 }); + if (error != 0) + apctlEvents.push_front({ oldState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); + else + apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_SCAN_COMPLETE, 0 }); break; case PSP_NET_APCTL_EVENT_SCAN_COMPLETE: newState = PSP_NET_APCTL_STATE_DISCONNECTED; if (error == 0) - apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_SCAN_STOP, 0 }); + apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_SCAN_STOP, 0 }); break; case PSP_NET_APCTL_EVENT_SCAN_STOP: @@ -510,20 +610,30 @@ void __NetApctlCallbacks() case PSP_NET_APCTL_EVENT_EAP_AUTH: // Is this suppose to happen between JOINING and ESTABLISHED ? newState = PSP_NET_APCTL_STATE_EAP_AUTH; - if (error == 0) - apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_KEY_EXCHANGE, 0 }); // not sure if KEY_EXCHANGE is the next step after AUTH or not tho + if (error != 0) + apctlEvents.push_front({ oldState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); + else + apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_KEY_EXCHANGE, 0 }); // not sure if KEY_EXCHANGE is the next step after AUTH or not tho break; case PSP_NET_APCTL_EVENT_KEY_EXCHANGE: // Is this suppose to happen between JOINING and ESTABLISHED ? newState = PSP_NET_APCTL_STATE_KEY_EXCHANGE; - if (error == 0) - apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); + if (error != 0) + apctlEvents.push_front({ oldState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); + else + apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); break; case PSP_NET_APCTL_EVENT_RECONNECT: newState = PSP_NET_APCTL_STATE_DISCONNECTED; - if (error == 0) - apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_CONNECT_REQUEST, 0 }); + if (error != 0) + apctlEvents.push_front({ oldState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_ERROR, error }); + else + apctlEvents.push_front({ oldState, newState, PSP_NET_APCTL_EVENT_CONNECT_REQUEST, 0 }); + break; + + case PSP_NET_APCTL_EVENT_ERROR: + newState = PSP_NET_APCTL_STATE_DISCONNECTED; break; } // Do we need to change the newState? even if there were error? @@ -703,7 +813,7 @@ static u32 sceWlanGetEtherAddr(u32 addrAddr) { static u32 sceNetGetLocalEtherAddr(u32 addrAddr) { // FIXME: Return 0x80410180 (pspnet[_core] error code?) before successful attempt to Create/Connect/Join a Group? (ie. adhocctlCurrentMode == ADHOCCTL_MODE_NONE) - if (adhocctlCurrentMode == ADHOCCTL_MODE_NONE) + if (adhocctlCurrentMode == ADHOCCTL_MODE_NONE && netApctlState == PSP_NET_APCTL_STATE_DISCONNECTED) return hleLogDebug(SCENET, 0x80410180, "address not available?"); return sceWlanGetEtherAddr(addrAddr); @@ -782,6 +892,980 @@ static void sceNetEtherStrton(u32 bufferPtr, u32 macPtr) { } +int convertMsgFlagPSP2Host(int flag) { + switch (flag) { + case PSP_NET_INET_MSG_OOB: + return MSG_OOB; + case PSP_NET_INET_MSG_PEEK: + return MSG_PEEK; + case PSP_NET_INET_MSG_DONTROUTE: + return MSG_DONTROUTE; +#if defined(MSG_EOR) + case PSP_NET_INET_MSG_EOR: + return MSG_EOR; +#endif + case PSP_NET_INET_MSG_TRUNC: + return MSG_TRUNC; + case PSP_NET_INET_MSG_CTRUNC: + return MSG_CTRUNC; + case PSP_NET_INET_MSG_WAITALL: + return MSG_WAITALL; +#if defined(MSG_DONTWAIT) + case PSP_NET_INET_MSG_DONTWAIT: + return MSG_DONTWAIT; +#endif +#if defined(MSG_BCAST) + case PSP_NET_INET_MSG_BCAST: + return MSG_BCAST; +#endif +#if defined(MSG_MCAST) + case PSP_NET_INET_MSG_MCAST: + return MSG_MCAST; +#endif + } + return hleLogError(SCENET, flag, "Unknown MSG flag"); +} + +int convertMsgFlagHost2PSP(int flag) { + switch (flag) { + case MSG_OOB: + return PSP_NET_INET_MSG_OOB; + case MSG_PEEK: + return PSP_NET_INET_MSG_PEEK; + case MSG_DONTROUTE: + return PSP_NET_INET_MSG_DONTROUTE; +#if defined(MSG_EOR) + case MSG_EOR: + return PSP_NET_INET_MSG_EOR; +#endif + case MSG_TRUNC: + return PSP_NET_INET_MSG_TRUNC; + case MSG_CTRUNC: + return PSP_NET_INET_MSG_CTRUNC; + case MSG_WAITALL: + return PSP_NET_INET_MSG_WAITALL; +#if defined(MSG_DONTWAIT) + case MSG_DONTWAIT: + return PSP_NET_INET_MSG_DONTWAIT; +#endif +#if defined(MSG_BCAST) + case MSG_BCAST: + return PSP_NET_INET_MSG_BCAST; +#endif +#if defined(MSG_MCAST) + case MSG_MCAST: + return PSP_NET_INET_MSG_MCAST; +#endif + } + return hleLogError(SCENET, flag, "Unknown MSG flag"); +} + +int convertMSGFlagsPSP2Host(int flags) { + // Only takes compatible one + int flgs = 0; + if (flags & PSP_NET_INET_MSG_OOB) { + flgs |= MSG_OOB; + } + if (flags & PSP_NET_INET_MSG_PEEK) { + flgs |= MSG_PEEK; + } + if (flags & PSP_NET_INET_MSG_DONTROUTE) { + flgs |= MSG_DONTROUTE; + } +#if defined(MSG_EOR) + if (flags & PSP_NET_INET_MSG_EOR) { + flgs |= MSG_EOR; + } +#endif + if (flags & PSP_NET_INET_MSG_TRUNC) { + flgs |= MSG_TRUNC; + } + if (flags & PSP_NET_INET_MSG_CTRUNC) { + flgs |= MSG_CTRUNC; + } + if (flags & PSP_NET_INET_MSG_WAITALL) { + flgs |= MSG_WAITALL; + } +#if defined(MSG_DONTWAIT) + if (flags & PSP_NET_INET_MSG_DONTWAIT) { + flgs |= MSG_DONTWAIT; + } +#endif +#if defined(MSG_BCAST) + if (flags & PSP_NET_INET_MSG_BCAST) { + flgs |= MSG_BCAST; + } +#endif +#if defined(MSG_MCAST) + if (flags & PSP_NET_INET_MSG_MCAST) { + flgs |= MSG_MCAST; + } +#endif + + return flgs; +} + +int convertMSGFlagsHost2PSP(int flags) { + // Only takes compatible one + int flgs = 0; + if (flags & MSG_OOB) { + flgs |= PSP_NET_INET_MSG_OOB; + } + if (flags & MSG_PEEK) { + flgs |= PSP_NET_INET_MSG_PEEK; + } + if (flags & MSG_DONTROUTE) { + flgs |= PSP_NET_INET_MSG_DONTROUTE; + } +#if defined(MSG_EOR) + if (flags & MSG_EOR) { + flgs |= PSP_NET_INET_MSG_EOR; + } +#endif + if (flags & MSG_TRUNC) { + flgs |= PSP_NET_INET_MSG_TRUNC; + } + if (flags & MSG_CTRUNC) { + flgs |= PSP_NET_INET_MSG_CTRUNC; + } + if (flags & MSG_WAITALL) { + flgs |= PSP_NET_INET_MSG_WAITALL; + } +#if defined(MSG_DONTWAIT) + if (flags & MSG_DONTWAIT) { + flgs |= PSP_NET_INET_MSG_DONTWAIT; + } +#endif +#if defined(MSG_BCAST) + if (flags & MSG_BCAST) { + flgs |= PSP_NET_INET_MSG_BCAST; + } +#endif +#if defined(MSG_MCAST) + if (flags & MSG_MCAST) { + flgs |= PSP_NET_INET_MSG_MCAST; + } +#endif + + return flgs; +} + +int convertSocketDomainPSP2Host(int domain) { + switch (domain) { + case PSP_NET_INET_AF_UNSPEC: + return AF_UNSPEC; + case PSP_NET_INET_AF_LOCAL: + return AF_UNIX; + case PSP_NET_INET_AF_INET: + return AF_INET; + } + return hleLogError(SCENET, domain, "Unknown Socket Domain"); +} + +int convertSocketDomainHost2PSP(int domain) { + switch (domain) { + case AF_UNSPEC: + return PSP_NET_INET_AF_UNSPEC; + case AF_UNIX: + return PSP_NET_INET_AF_LOCAL; + case AF_INET: + return PSP_NET_INET_AF_INET; + } + return hleLogError(SCENET, domain, "Unknown Socket Domain"); +} + +std::string inetSocketDomain2str(int domain) { + switch (domain) { + case PSP_NET_INET_AF_UNSPEC: + return "AF_UNSPEC"; + case PSP_NET_INET_AF_UNIX: + return "AF_UNIX"; + case PSP_NET_INET_AF_INET: + return "AF_INET"; + } + return "AF_" + StringFromFormat("%08x", domain); +} + +int convertSocketTypePSP2Host(int type) { + // FIXME: Masked with 0x0F since there might be additional flags mixed in socket type that need to be converted too + switch (type & PSP_NET_INET_SOCK_TYPE_MASK) { + case PSP_NET_INET_SOCK_STREAM: + return SOCK_STREAM; + case PSP_NET_INET_SOCK_DGRAM: + return SOCK_DGRAM; + case PSP_NET_INET_SOCK_RAW: + // FIXME: SOCK_RAW have some restrictions on newer Windows? + return SOCK_RAW; + case PSP_NET_INET_SOCK_RDM: + return SOCK_RDM; + case PSP_NET_INET_SOCK_SEQPACKET: + return SOCK_SEQPACKET; + case PSP_NET_INET_SOCK_CONN_DGRAM: // PSP_NET_INET_SOCK_DCCP? + return SOCK_DGRAM; // SOCK_RAW? + case PSP_NET_INET_SOCK_PACKET: + return SOCK_STREAM; // SOCK_RAW? + } + + return hleLogError(SCENET, type, "Unknown Socket Type") & PSP_NET_INET_SOCK_TYPE_MASK; +} + +int convertSocketTypeHost2PSP(int type) { + // FIXME: Masked with 0x0F since there might be additional flags mixed in socket type that need to be converted too + switch (type & PSP_NET_INET_SOCK_TYPE_MASK) { + case SOCK_STREAM: + return PSP_NET_INET_SOCK_STREAM; + case SOCK_DGRAM: + return PSP_NET_INET_SOCK_DGRAM; + case SOCK_RAW: + return PSP_NET_INET_SOCK_RAW; + case SOCK_RDM: + return PSP_NET_INET_SOCK_RDM; + case SOCK_SEQPACKET: + return PSP_NET_INET_SOCK_SEQPACKET; +#if defined(CONN_DGRAM) + case CONN_DGRAM: // SOCK_DCCP + return PSP_NET_INET_SOCK_CONN_DGRAM; // PSP_NET_INET_SOCK_DCCP +#endif +#if defined(SOCK_PACKET) + case SOCK_PACKET: + return PSP_NET_INET_SOCK_PACKET; +#endif + } + + return hleLogError(SCENET, type, "Unknown Socket Type") & PSP_NET_INET_SOCK_TYPE_MASK; +} + +std::string inetSocketType2str(int type) { + switch (type & PSP_NET_INET_SOCK_TYPE_MASK) { + case PSP_NET_INET_SOCK_STREAM: + return "SOCK_STREAM"; + case PSP_NET_INET_SOCK_DGRAM: + return "SOCK_DGRAM"; + case PSP_NET_INET_SOCK_RAW: + return "SOCK_RAW"; + case PSP_NET_INET_SOCK_RDM: + return "SOCK_RDM"; + case PSP_NET_INET_SOCK_SEQPACKET: + return "SOCK_SEQPACKET"; + case PSP_NET_INET_SOCK_DCCP: + return "SOCK_DCCP/SOCK_CONN_DGRAM?"; + case PSP_NET_INET_SOCK_PACKET: + return "SOCK_PACKET?"; + } + return "SOCK_" + StringFromFormat("%08x", type); +} + +int convertSocketProtoPSP2Host(int protocol) { + switch (protocol) { + case PSP_NET_INET_IPPROTO_UNSPEC: + return PSP_NET_INET_IPPROTO_UNSPEC; // 0 only valid if there is only 1 protocol available for a particular domain/family and type? + case PSP_NET_INET_IPPROTO_ICMP: + return IPPROTO_ICMP; + case PSP_NET_INET_IPPROTO_IGMP: + return IPPROTO_IGMP; + case PSP_NET_INET_IPPROTO_TCP: + return IPPROTO_TCP; + case PSP_NET_INET_IPPROTO_EGP: + return IPPROTO_EGP; + case PSP_NET_INET_IPPROTO_PUP: + return IPPROTO_PUP; + case PSP_NET_INET_IPPROTO_UDP: + return IPPROTO_UDP; + case PSP_NET_INET_IPPROTO_IDP: + return IPPROTO_IDP; + case PSP_NET_INET_IPPROTO_RAW: + return IPPROTO_RAW; + } + return hleLogError(SCENET, protocol, "Unknown Socket Protocol"); +} + +int convertSocketProtoHost2PSP(int protocol) { + switch (protocol) { + case PSP_NET_INET_IPPROTO_UNSPEC: + return PSP_NET_INET_IPPROTO_UNSPEC; // 0 only valid if there is only 1 protocol available for a particular domain/family and type? + case IPPROTO_ICMP: + return PSP_NET_INET_IPPROTO_ICMP; + case IPPROTO_IGMP: + return PSP_NET_INET_IPPROTO_IGMP; + case IPPROTO_TCP: + return PSP_NET_INET_IPPROTO_TCP; + case IPPROTO_EGP: + return PSP_NET_INET_IPPROTO_EGP; + case IPPROTO_PUP: + return PSP_NET_INET_IPPROTO_PUP; + case IPPROTO_UDP: + return PSP_NET_INET_IPPROTO_UDP; + case IPPROTO_IDP: + return PSP_NET_INET_IPPROTO_IDP; + case IPPROTO_RAW: + return PSP_NET_INET_IPPROTO_RAW; + } + return hleLogError(SCENET, protocol, "Unknown Socket Protocol"); +} + +std::string inetSocketProto2str(int protocol) { + switch (protocol) { + case PSP_NET_INET_IPPROTO_UNSPEC: + return "IPPROTO_UNSPEC (DEFAULT?)"; // defaulted to IPPROTO_TCP for SOCK_STREAM and IPPROTO_UDP for SOCK_DGRAM + case PSP_NET_INET_IPPROTO_ICMP: + return "IPPROTO_ICMP"; + case PSP_NET_INET_IPPROTO_IGMP: + return "IPPROTO_IGMP"; + case PSP_NET_INET_IPPROTO_TCP: + return "IPPROTO_TCP"; + case PSP_NET_INET_IPPROTO_EGP: + return "IPPROTO_EGP"; + case PSP_NET_INET_IPPROTO_PUP: + return "IPPROTO_PUP"; + case PSP_NET_INET_IPPROTO_UDP: + return "IPPROTO_UDP"; + case PSP_NET_INET_IPPROTO_IDP: + return "IPPROTO_IDP"; + case PSP_NET_INET_IPPROTO_RAW: + return "IPPROTO_RAW"; + } + return "IPPROTO_" + StringFromFormat("%08x", protocol); +} + +int convertCMsgTypePSP2Host(int type, int level) { + if (level == PSP_NET_INET_IPPROTO_IP) { + switch (type) { +#if defined(IP_RECVDSTADDR) + case PSP_NET_INET_IP_RECVDSTADDR: + return IP_RECVDSTADDR; +#endif +#if defined(IP_RECVIF) + case PSP_NET_INET_IP_RECVIF: + return IP_RECVIF; +#endif + } + } + else if (level == PSP_NET_INET_SOL_SOCKET) { +#if defined(SCM_RIGHTS) + if (type == PSP_NET_INET_SCM_RIGHTS) + return SCM_RIGHTS; +#endif +#if defined(SCM_CREDS) + if (type == PSP_NET_INET_SCM_CREDS) + return SCM_CREDS; +#endif +#if defined(SCM_TIMESTAMP) + if (type == PSP_NET_INET_SCM_TIMESTAMP) + return SCM_TIMESTAMP; +#endif + } + return hleLogError(SCENET, type, "Unknown CMSG_TYPE (Level = %08x)", level); +} + +int convertCMsgTypeHost2PSP(int type, int level) { + if (level == IPPROTO_IP) { + switch (type) { +#if defined(IP_RECVDSTADDR) + case IP_RECVDSTADDR: + return PSP_NET_INET_IP_RECVDSTADDR; +#endif +#if defined(IP_RECVIF) + case IP_RECVIF: + return PSP_NET_INET_IP_RECVIF; +#endif + } + } + else if (level == SOL_SOCKET) { +#if defined(SCM_RIGHTS) + if (type == SCM_RIGHTS) + return PSP_NET_INET_SCM_RIGHTS; +#endif +#if defined(SCM_CREDS) + if (type == SCM_CREDS) + return PSP_NET_INET_SCM_CREDS; +#endif +#if defined(SCM_TIMESTAMP) + if (type == SCM_TIMESTAMP) + return PSP_NET_INET_SCM_TIMESTAMP; +#endif + } + return hleLogError(SCENET, type, "Unknown CMSG_TYPE (Level = %08x)", level); +} + +int convertSockoptLevelPSP2Host(int level) { + switch (level) { + case PSP_NET_INET_IPPROTO_IP: + return IPPROTO_IP; + case PSP_NET_INET_IPPROTO_TCP: + return IPPROTO_TCP; + case PSP_NET_INET_IPPROTO_UDP: + return IPPROTO_UDP; + case PSP_NET_INET_SOL_SOCKET: + return SOL_SOCKET; + } + return hleLogError(SCENET, level, "Unknown SockOpt Level"); +} + +int convertSockoptLevelHost2PSP(int level) { + switch (level) { + case IPPROTO_IP: + return PSP_NET_INET_IPPROTO_IP; + case IPPROTO_TCP: + return PSP_NET_INET_IPPROTO_TCP; + case IPPROTO_UDP: + return PSP_NET_INET_IPPROTO_UDP; + case SOL_SOCKET: + return PSP_NET_INET_SOL_SOCKET; + } + return hleLogError(SCENET, level, "Unknown SockOpt Level"); +} + +std::string inetSockoptLevel2str(int level) { + switch (level) { + case PSP_NET_INET_IPPROTO_IP: + return "IPPROTO_IP"; + case PSP_NET_INET_IPPROTO_TCP: + return "IPPROTO_TCP"; + case PSP_NET_INET_IPPROTO_UDP: + return "IPPROTO_UDP"; + case PSP_NET_INET_SOL_SOCKET: + return "SOL_SOCKET"; + } + return "SOL_" + StringFromFormat("%08x", level); +} + +int convertSockoptNamePSP2Host(int optname, int level) { + if (level == PSP_NET_INET_IPPROTO_TCP) { + switch (optname) { + case PSP_NET_INET_TCP_NODELAY: + return TCP_NODELAY; + case PSP_NET_INET_TCP_MAXSEG: + return TCP_MAXSEG; + } + } + else if (level == PSP_NET_INET_IPPROTO_IP) { + switch (optname) { + case PSP_NET_INET_IP_OPTIONS: + return IP_OPTIONS; + case PSP_NET_INET_IP_HDRINCL: + return IP_HDRINCL; + case PSP_NET_INET_IP_TOS: + return IP_TOS; + case PSP_NET_INET_IP_TTL: + return IP_TTL; +#if defined(IP_RECVOPTS) + case PSP_NET_INET_IP_RECVOPTS: + return IP_RECVOPTS; +#endif +#if defined(IP_RECVRETOPTS) + case PSP_NET_INET_IP_RECVRETOPTS: + return IP_RECVRETOPTS; +#endif +#if defined(IP_RECVDSTADDR) + case PSP_NET_INET_IP_RECVDSTADDR: + return IP_RECVDSTADDR; +#endif +#if defined(IP_RETOPTS) + case PSP_NET_INET_IP_RETOPTS: + return IP_RETOPTS; +#endif + case PSP_NET_INET_IP_MULTICAST_IF: + return IP_MULTICAST_IF; + case PSP_NET_INET_IP_MULTICAST_TTL: + return IP_MULTICAST_TTL; + case PSP_NET_INET_IP_MULTICAST_LOOP: + return IP_MULTICAST_LOOP; + case PSP_NET_INET_IP_ADD_MEMBERSHIP: + return IP_ADD_MEMBERSHIP; + case PSP_NET_INET_IP_DROP_MEMBERSHIP: + return IP_DROP_MEMBERSHIP; +#if defined(IP_PORTRANGE) + case PSP_NET_INET_IP_PORTRANGE: + return IP_PORTRANGE; +#endif +#if defined(IP_RECVIF) + case PSP_NET_INET_IP_RECVIF: + return IP_RECVIF; +#endif +#if defined(IP_ERRORMTU) + case PSP_NET_INET_IP_ERRORMTU: + return IP_ERRORMTU; +#endif +#if defined(IP_IPSEC_POLICY) + case PSP_NET_INET_IP_IPSEC_POLICY: + return IP_IPSEC_POLICY; +#endif + } + } + else if (level == PSP_NET_INET_SOL_SOCKET) { + switch (optname) { + case PSP_NET_INET_SO_DEBUG: + return SO_DEBUG; + case PSP_NET_INET_SO_ACCEPTCONN: + return SO_ACCEPTCONN; + case PSP_NET_INET_SO_REUSEADDR: + return SO_REUSEADDR; + case PSP_NET_INET_SO_KEEPALIVE: + return SO_KEEPALIVE; + case PSP_NET_INET_SO_DONTROUTE: + return SO_DONTROUTE; + case PSP_NET_INET_SO_BROADCAST: + return SO_BROADCAST; +#if defined(SO_USELOOPBACK) + case PSP_NET_INET_SO_USELOOPBACK: + return SO_USELOOPBACK; +#endif + case PSP_NET_INET_SO_LINGER: + return SO_LINGER; + case PSP_NET_INET_SO_OOBINLINE: + return SO_OOBINLINE; +#if defined(SO_REUSEPORT) + case PSP_NET_INET_SO_REUSEPORT: + return SO_REUSEPORT; +#endif +#if defined(SO_TIMESTAMP) + case PSP_NET_INET_SO_TIMESTAMP: + return SO_TIMESTAMP; +#endif +#if defined(SO_ONESBCAST) + case PSP_NET_INET_SO_ONESBCAST: + return SO_ONESBCAST; +#endif + case PSP_NET_INET_SO_SNDBUF: + return SO_SNDBUF; + case PSP_NET_INET_SO_RCVBUF: + return SO_RCVBUF; + case PSP_NET_INET_SO_SNDLOWAT: + return SO_SNDLOWAT; + case PSP_NET_INET_SO_RCVLOWAT: + return SO_RCVLOWAT; + case PSP_NET_INET_SO_SNDTIMEO: + return SO_SNDTIMEO; + case PSP_NET_INET_SO_RCVTIMEO: + return SO_RCVTIMEO; + case PSP_NET_INET_SO_ERROR: + return SO_ERROR; + case PSP_NET_INET_SO_TYPE: + return SO_TYPE; +#if defined(SO_NBIO) + case PSP_NET_INET_SO_NBIO: + return SO_NBIO; +#endif +#if defined(SO_BIO) + case PSP_NET_INET_SO_BIO: + return SO_BIO; +#endif + } + } + return hleLogError(SCENET, optname, "Unknown PSP's SockOpt Name (Level = %08x)", level); +} + +int convertSockoptNameHost2PSP(int optname, int level) { + if (level == IPPROTO_TCP) { + switch (optname) { + case TCP_NODELAY: + return PSP_NET_INET_TCP_NODELAY; + case TCP_MAXSEG: + return PSP_NET_INET_TCP_MAXSEG; + } + } + else if (level == IPPROTO_IP) { + switch (optname) { + case IP_OPTIONS: + return PSP_NET_INET_IP_OPTIONS; + case IP_HDRINCL: + return PSP_NET_INET_IP_HDRINCL; + case IP_TOS: + return PSP_NET_INET_IP_TOS; + case IP_TTL: + return PSP_NET_INET_IP_TTL; +#if defined(IP_RECVOPTS) + case IP_RECVOPTS: + return PSP_NET_INET_IP_RECVOPTS; +#endif +#if defined(IP_RECVRETOPTS) && (IP_RECVRETOPTS != IP_RETOPTS) + case IP_RECVRETOPTS: + return PSP_NET_INET_IP_RECVRETOPTS; +#endif +#if defined(IP_RECVDSTADDR) + case IP_RECVDSTADDR: + return PSP_NET_INET_IP_RECVDSTADDR; +#endif +#if defined(IP_RETOPTS) + case IP_RETOPTS: + return PSP_NET_INET_IP_RETOPTS; +#endif + case IP_MULTICAST_IF: + return PSP_NET_INET_IP_MULTICAST_IF; + case IP_MULTICAST_TTL: + return PSP_NET_INET_IP_MULTICAST_TTL; + case IP_MULTICAST_LOOP: + return PSP_NET_INET_IP_MULTICAST_LOOP; + case IP_ADD_MEMBERSHIP: + return PSP_NET_INET_IP_ADD_MEMBERSHIP; + case IP_DROP_MEMBERSHIP: + return PSP_NET_INET_IP_DROP_MEMBERSHIP; +#if defined(IP_PORTRANGE) + case IP_PORTRANGE: + return PSP_NET_INET_IP_PORTRANGE; +#endif +#if defined(IP_RECVIF) + case PSP_NET_INET_IP_RECVIF: + return IP_RECVIF; +#endif +#if defined(IP_ERRORMTU) + case IP_ERRORMTU: + return PSP_NET_INET_IP_ERRORMTU; +#endif +#if defined(IP_IPSEC_POLICY) + case IP_IPSEC_POLICY: + return PSP_NET_INET_IP_IPSEC_POLICY; +#endif + } + } + else if (level == SOL_SOCKET) { + switch (optname) { + case SO_DEBUG: + return PSP_NET_INET_SO_DEBUG; + case SO_ACCEPTCONN: + return PSP_NET_INET_SO_ACCEPTCONN; + case SO_REUSEADDR: + return PSP_NET_INET_SO_REUSEADDR; + case SO_KEEPALIVE: + return PSP_NET_INET_SO_KEEPALIVE; + case SO_DONTROUTE: + return PSP_NET_INET_SO_DONTROUTE; + case SO_BROADCAST: + return PSP_NET_INET_SO_BROADCAST; +#if defined(SO_USELOOPBACK) + case SO_USELOOPBACK: + return PSP_NET_INET_SO_USELOOPBACK; +#endif + case SO_LINGER: + return PSP_NET_INET_SO_LINGER; + case SO_OOBINLINE: + return PSP_NET_INET_SO_OOBINLINE; +#if defined(SO_REUSEPORT) + case SO_REUSEPORT: + return PSP_NET_INET_SO_REUSEPORT; +#endif +#if defined(SO_TIMESTAMP) + case SO_TIMESTAMP: + return PSP_NET_INET_SO_TIMESTAMP; +#endif +#if defined(SO_ONESBCAST) + case SO_ONESBCAST: + return PSP_NET_INET_SO_ONESBCAST; +#endif + case SO_SNDBUF: + return PSP_NET_INET_SO_SNDBUF; + case SO_RCVBUF: + return PSP_NET_INET_SO_RCVBUF; + case SO_SNDLOWAT: + return PSP_NET_INET_SO_SNDLOWAT; + case SO_RCVLOWAT: + return PSP_NET_INET_SO_RCVLOWAT; + case SO_SNDTIMEO: + return PSP_NET_INET_SO_SNDTIMEO; + case SO_RCVTIMEO: + return PSP_NET_INET_SO_RCVTIMEO; + case SO_ERROR: + return PSP_NET_INET_SO_ERROR; + case SO_TYPE: + return PSP_NET_INET_SO_TYPE; +#if defined(SO_NBIO) + case SO_NBIO: + return PSP_NET_INET_SO_NBIO; +#endif +#if defined(SO_BIO) + case SO_BIO: + return PSP_NET_INET_SO_BIO; +#endif + } + } + return hleLogError(SCENET, optname, "Unknown Host's SockOpt Name (Level = %08x)", level); +} + +std::string inetSockoptName2str(int optname, int level) { + if (level == PSP_NET_INET_IPPROTO_TCP) { + switch (optname) { + case PSP_NET_INET_TCP_NODELAY: + return "TCP_NODELAY"; + case PSP_NET_INET_TCP_MAXSEG: + return "TCP_MAXSEG"; + } + } + else if (level == PSP_NET_INET_IPPROTO_IP) { + switch (optname) { + case PSP_NET_INET_IP_OPTIONS: + return "IP_OPTIONS"; + case PSP_NET_INET_IP_HDRINCL: + return "IP_HDRINCL"; + case PSP_NET_INET_IP_TOS: + return "IP_TOS"; + case PSP_NET_INET_IP_TTL: + return "IP_TTL"; + case PSP_NET_INET_IP_RECVOPTS: + return "IP_RECVOPTS"; + case PSP_NET_INET_IP_RECVRETOPTS: + return "IP_RECVRETOPTS"; + case PSP_NET_INET_IP_RECVDSTADDR: + return "IP_RECVDSTADDR"; + case PSP_NET_INET_IP_RETOPTS: + return "IP_RETOPTS"; + case PSP_NET_INET_IP_MULTICAST_IF: + return "IP_MULTICAST_IF"; + case PSP_NET_INET_IP_MULTICAST_TTL: + return "IP_MULTICAST_TTL"; + case PSP_NET_INET_IP_MULTICAST_LOOP: + return "IP_MULTICAST_LOOP"; + case PSP_NET_INET_IP_ADD_MEMBERSHIP: + return "IP_ADD_MEMBERSHIP"; + case PSP_NET_INET_IP_DROP_MEMBERSHIP: + return "IP_DROP_MEMBERSHIP"; + case PSP_NET_INET_IP_PORTRANGE: + return "IP_PORTRANGE"; + case PSP_NET_INET_IP_RECVIF: + return "IP_RECVIF"; + case PSP_NET_INET_IP_ERRORMTU: + return "IP_ERRORMTU"; + case PSP_NET_INET_IP_IPSEC_POLICY: + return "IP_IPSEC_POLICY"; + } + } + else if (level == PSP_NET_INET_SOL_SOCKET) { + switch (optname) { + case PSP_NET_INET_SO_DEBUG: + return "SO_DEBUG"; + case PSP_NET_INET_SO_ACCEPTCONN: + return "SO_ACCEPTCONN"; + case PSP_NET_INET_SO_REUSEADDR: + return "SO_REUSEADDR"; + case PSP_NET_INET_SO_KEEPALIVE: + return "SO_KEEPALIVE"; + case PSP_NET_INET_SO_DONTROUTE: + return "SO_DONTROUTE"; + case PSP_NET_INET_SO_BROADCAST: + return "SO_BROADCAST"; + case PSP_NET_INET_SO_USELOOPBACK: + return "SO_USELOOPBACK"; + case PSP_NET_INET_SO_LINGER: + return "SO_LINGER"; + case PSP_NET_INET_SO_OOBINLINE: + return "SO_OOBINLINE"; + case PSP_NET_INET_SO_REUSEPORT: + return "SO_REUSEPORT"; + case PSP_NET_INET_SO_TIMESTAMP: + return "SO_TIMESTAMP"; + case PSP_NET_INET_SO_ONESBCAST: + return "SO_ONESBCAST"; + case PSP_NET_INET_SO_SNDBUF: + return "SO_SNDBUF"; + case PSP_NET_INET_SO_RCVBUF: + return "SO_RCVBUF"; + case PSP_NET_INET_SO_SNDLOWAT: + return "SO_SNDLOWAT"; + case PSP_NET_INET_SO_RCVLOWAT: + return "SO_RCVLOWAT"; + case PSP_NET_INET_SO_SNDTIMEO: + return "SO_SNDTIMEO"; + case PSP_NET_INET_SO_RCVTIMEO: + return "SO_RCVTIMEO"; + case PSP_NET_INET_SO_ERROR: + return "SO_ERROR"; + case PSP_NET_INET_SO_TYPE: + return "SO_TYPE"; + case PSP_NET_INET_SO_NBIO: + return "SO_NBIO"; // SO_NONBLOCK + case PSP_NET_INET_SO_BIO: + return "SO_BIO"; + } + } + return "SO_" + StringFromFormat("%08x (Level = %08x)", optname, level); +} + +int convertInetErrnoHost2PSP(int error) { + switch (error) { + case EINTR: + return ERROR_INET_EINTR; + case EBADF: + return ERROR_INET_EBADF; + case EACCES: + return ERROR_INET_EACCES; + case EFAULT: + return ERROR_INET_EFAULT; + case EINVAL: + return ERROR_INET_EINVAL; + case ENOSPC: + return ERROR_INET_ENOSPC; + case EPIPE: + return ERROR_INET_EPIPE; + case ENOMSG: + return ERROR_INET_ENOMSG; + case ENOLINK: + return ERROR_INET_ENOLINK; + case EPROTO: + return ERROR_INET_EPROTO; + case EBADMSG: + return ERROR_INET_EBADMSG; + case EOPNOTSUPP: + return ERROR_INET_EOPNOTSUPP; + case EPFNOSUPPORT: + return ERROR_INET_EPFNOSUPPORT; + case ECONNRESET: + return ERROR_INET_ECONNRESET; + case ENOBUFS: + return ERROR_INET_ENOBUFS; + case EAFNOSUPPORT: + return ERROR_INET_EAFNOSUPPORT; + case EPROTOTYPE: + return ERROR_INET_EPROTOTYPE; + case ENOTSOCK: + return ERROR_INET_ENOTSOCK; + case ENOPROTOOPT: + return ERROR_INET_ENOPROTOOPT; + case ESHUTDOWN: + return ERROR_INET_ESHUTDOWN; + case ECONNREFUSED: + return ERROR_INET_ECONNREFUSED; + case EADDRINUSE: + return ERROR_INET_EADDRINUSE; + case ECONNABORTED: + return ERROR_INET_ECONNABORTED; + case ENETUNREACH: + return ERROR_INET_ENETUNREACH; + case ENETDOWN: + return ERROR_INET_ENETDOWN; + case ETIMEDOUT: + return ERROR_INET_ETIMEDOUT; + case EHOSTDOWN: + return ERROR_INET_EHOSTDOWN; + case EHOSTUNREACH: + return ERROR_INET_EHOSTUNREACH; + case EALREADY: + return ERROR_INET_EALREADY; + case EMSGSIZE: + return ERROR_INET_EMSGSIZE; + case EPROTONOSUPPORT: + return ERROR_INET_EPROTONOSUPPORT; + case ESOCKTNOSUPPORT: + return ERROR_INET_ESOCKTNOSUPPORT; + case EADDRNOTAVAIL: + return ERROR_INET_EADDRNOTAVAIL; + case ENETRESET: + return ERROR_INET_ENETRESET; + case EISCONN: + return ERROR_INET_EISCONN; + case ENOTCONN: + return ERROR_INET_ENOTCONN; + case EAGAIN: + return ERROR_INET_EAGAIN; +#if !defined(_WIN32) + case EINPROGRESS: + return ERROR_INET_EINPROGRESS; +#endif + } + if (error != 0) + return hleLogError(SCENET, error, "Unknown Error Number (%d)", error); + return error; +} + +// FIXME: Some of this might be wrong +int convertInetErrno2PSPError(int error) { + switch (error) { + case ERROR_INET_EINTR: + return SCE_KERNEL_ERROR_ERRNO_DEVICE_BUSY; + case ERROR_INET_EACCES: + return SCE_KERNEL_ERROR_ERRNO_READ_ONLY; + case ERROR_INET_EFAULT: + return SCE_KERNEL_ERROR_ERRNO_ADDR_OUT_OF_MAIN_MEM; + case ERROR_INET_EINVAL: + return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT; + case ERROR_INET_ENOSPC: + return SCE_KERNEL_ERROR_ERRNO_NO_MEMORY; + case ERROR_INET_EPIPE: + return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND; + case ERROR_INET_ENOMSG: + return SCE_KERNEL_ERROR_ERRNO_NO_MEDIA; + case ERROR_INET_ENOLINK: + return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; + case ERROR_INET_EPROTO: + return SCE_KERNEL_ERROR_ERRNO_FILE_PROTOCOL; + case ERROR_INET_EBADMSG: + return SCE_KERNEL_ERROR_ERRNO_INVALID_MEDIUM; + case ERROR_INET_EOPNOTSUPP: + return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED; + case ERROR_INET_EPFNOSUPPORT: + return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED; + case ERROR_INET_ECONNRESET: + return SCE_KERNEL_ERROR_ERRNO_CONNECTION_RESET; + case ERROR_INET_ENOBUFS: + return SCE_KERNEL_ERROR_ERRNO_NO_FREE_BUF_SPACE; + case ERROR_INET_EAFNOSUPPORT: + return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED; + case ERROR_INET_EPROTOTYPE: + return SCE_KERNEL_ERROR_ERRNO_FILE_PROTOCOL; + case ERROR_INET_ENOTSOCK: + return SCE_KERNEL_ERROR_ERRNO_INVALID_FILE_DESCRIPTOR; + case ERROR_INET_ENOPROTOOPT: + return SCE_KERNEL_ERROR_ERRNO_FILE_PROTOCOL; + case ERROR_INET_ESHUTDOWN: + return SCE_KERNEL_ERROR_ERRNO_CLOSED; + case ERROR_INET_ECONNREFUSED: + return SCE_KERNEL_ERROR_ERRNO_FILE_ALREADY_EXISTS; + case ERROR_INET_EADDRINUSE: + return SCE_KERNEL_ERROR_ERRNO_FILE_ADDR_IN_USE; + case ERROR_INET_ECONNABORTED: + return SCE_KERNEL_ERROR_ERRNO_CONNECTION_ABORTED; + case ERROR_INET_ENETUNREACH: + return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; + case ERROR_INET_ENETDOWN: + return SCE_KERNEL_ERROR_ERRNO_CLOSED; + case ERROR_INET_ETIMEDOUT: + return SCE_KERNEL_ERROR_ERRNO_FILE_TIMEOUT; + case ERROR_INET_EHOSTDOWN: + return SCE_KERNEL_ERROR_ERRNO_CLOSED; + case ERROR_INET_EHOSTUNREACH: + return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND; + case ERROR_INET_EALREADY: + return SCE_KERNEL_ERROR_ERRNO_ALREADY; + case ERROR_INET_EMSGSIZE: + return SCE_KERNEL_ERROR_ERRNO_FILE_IS_TOO_BIG; + case ERROR_INET_EPROTONOSUPPORT: + return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED; + case ERROR_INET_ESOCKTNOSUPPORT: + return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED; + case ERROR_INET_EADDRNOTAVAIL: + return SCE_KERNEL_ERROR_ERRNO_ADDRESS_NOT_AVAILABLE; + case ERROR_INET_ENETRESET: + return SCE_KERNEL_ERROR_ERRNO_CONNECTION_RESET; + case ERROR_INET_EISCONN: + return SCE_KERNEL_ERROR_ERRNO_ALREADY; // SCE_KERNEL_ERROR_ERRNO_IS_ALREADY_CONNECTED; // UNO only check for 0x80010077 and 0x80010078 + case ERROR_INET_ENOTCONN: + return SCE_KERNEL_ERROR_ERRNO_NOT_CONNECTED; + case ERROR_INET_EAGAIN: + return SCE_KERNEL_ERROR_ERRNO_RESOURCE_UNAVAILABLE; // SCE_ERROR_ERRNO_EAGAIN; +#if !defined(_WIN32) + case ERROR_INET_EINPROGRESS: + return SCE_KERNEL_ERROR_ERRNO_IN_PROGRESS; +#endif + } + if (error != 0) + return hleLogError(SCENET, error, "Unknown PSP Error Number (%d)", error); + return error; +} + +static int sceNetInetGetErrno() { + if (inetLastErrno == 0) + inetLastErrno = errno; + int error = convertInetErrnoHost2PSP(inetLastErrno); + inetLastErrno = 0; + return hleLogSuccessI(SCENET, error, " at %08x", currentMIPS->pc); +} + +static int sceNetInetGetPspError() { + if (inetLastErrno == 0) + inetLastErrno = errno; + int error = convertInetErrno2PSPError(convertInetErrnoHost2PSP(inetLastErrno)); + inetLastErrno = 0; + return hleLogSuccessX(SCENET, error, " at %08x", currentMIPS->pc); +} + // Write static data since we don't actually manage any memory for sceNet* yet. static int sceNetGetMallocStat(u32 statPtr) { VERBOSE_LOG(SCENET, "UNTESTED sceNetGetMallocStat(%x) at %08x", statPtr, currentMIPS->pc); @@ -805,18 +1889,34 @@ static int sceNetInetInit() { int sceNetInetTerm() { ERROR_LOG(SCENET, "UNIMPL sceNetInetTerm()"); netInetInited = false; - + // FIXME: Should we Shutdown/Close previously created sockets so we can reuse the ports on the next MP session? return 0; } -void NetApctl_InitInfo() { +void NetApctl_InitDefaultInfo() { memset(&netApctlInfo, 0, sizeof(netApctlInfo)); - // Set dummy/fake values, these probably not suppose to have valid info before connected to an AP, right? - std::string APname = "Wifi"; // fake AP/hotspot - truncate_cpy(netApctlInfo.name, sizeof(netApctlInfo.name), APname.c_str()); - truncate_cpy(netApctlInfo.ssid, sizeof(netApctlInfo.ssid), APname.c_str()); + // Set dummy/fake parameters, assuming this is the currently selected Network Configuration profile + // FIXME: Some of these info supposed to be taken from netConfInfo + int validConfId = std::max(1, netApctlInfoId); // Should be: sceUtilityGetNetParamLatestID(validConfId); + truncate_cpy(netApctlInfo.name, sizeof(netApctlInfo.name), (defaultNetConfigName + std::to_string(validConfId)).c_str()); + truncate_cpy(netApctlInfo.ssid, sizeof(netApctlInfo.ssid), defaultNetSSID.c_str()); + // Default IP Address + char ipstr[INET_ADDRSTRLEN] = "0.0.0.0"; // Driver 76 needs a dot formatted IP instead of a zeroed buffer + truncate_cpy(netApctlInfo.ip, sizeof(netApctlInfo.ip), ipstr); + truncate_cpy(netApctlInfo.gateway, sizeof(netApctlInfo.gateway), ipstr); + truncate_cpy(netApctlInfo.primaryDns, sizeof(netApctlInfo.primaryDns), ipstr); + truncate_cpy(netApctlInfo.secondaryDns, sizeof(netApctlInfo.secondaryDns), ipstr); + truncate_cpy(netApctlInfo.subNetMask, sizeof(netApctlInfo.subNetMask), ipstr); +} + +void NetApctl_InitInfo(int confId) { + memset(&netApctlInfo, 0, sizeof(netApctlInfo)); + // Set dummy/fake values, some of these (ie. IP set to Auto) probably not suppose to have valid info before connected to an AP, right? + // FIXME: Some of these info supposed to be taken from netConfInfo + truncate_cpy(netApctlInfo.name, sizeof(netApctlInfo.name), (defaultNetConfigName + std::to_string(confId)).c_str()); + truncate_cpy(netApctlInfo.ssid, sizeof(netApctlInfo.ssid), defaultNetSSID.c_str()); memcpy(netApctlInfo.bssid, "\1\1\2\2\3\3", sizeof(netApctlInfo.bssid)); // fake AP's mac address - netApctlInfo.ssidLength = static_cast(APname.length()); + netApctlInfo.ssidLength = static_cast(defaultNetSSID.length()); netApctlInfo.strength = 99; netApctlInfo.channel = g_Config.iWlanAdhocChannel; if (netApctlInfo.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) netApctlInfo.channel = defaultWlanChannel; @@ -830,8 +1930,12 @@ void NetApctl_InitInfo() { ((u8*)&sockAddr.sin_addr.s_addr)[3] = 1; inet_ntop(AF_INET, &sockAddr.sin_addr, ipstr, sizeof(ipstr)); truncate_cpy(netApctlInfo.gateway, sizeof(netApctlInfo.gateway), ipstr); - truncate_cpy(netApctlInfo.primaryDns, sizeof(netApctlInfo.primaryDns), ipstr); - truncate_cpy(netApctlInfo.secondaryDns, sizeof(netApctlInfo.secondaryDns), "8.8.8.8"); + // We should probably use public DNS Server instead of localhost IP since most people don't have DNS Server running on localhost (ie. Untold Legends The Warrior's Code is trying to lookup dns using the primary dns), but accessing public DNS Server from localhost may result to ENETUNREACH error if the gateway can't access the public server (ie. using SO_DONTROUTE) + //if (strcmp(ipstr, "127.0.0.1") == 0) + truncate_cpy(netApctlInfo.primaryDns, sizeof(netApctlInfo.primaryDns), g_Config.primaryDNSServer.c_str()); // Private Servers may need to use custom DNS Server + //else + // truncate_cpy(netApctlInfo.primaryDns, sizeof(netApctlInfo.primaryDns), ipstr); + truncate_cpy(netApctlInfo.secondaryDns, sizeof(netApctlInfo.secondaryDns), g_Config.secondaryDNSServer.c_str()); // Fireteam Bravo 2 seems to try to use secondary DNS too if it's not 0.0.0.0 truncate_cpy(netApctlInfo.subNetMask, sizeof(netApctlInfo.subNetMask), "255.255.255.0"); } @@ -844,17 +1948,7 @@ static int sceNetApctlInit(int stackSize, int initPriority) { netApctlState = PSP_NET_APCTL_STATE_DISCONNECTED; // Set default value before connected to an AP - memset(&netApctlInfo, 0, sizeof(netApctlInfo)); // NetApctl_InitInfo(); - std::string APname = "Wifi"; // fake AP/hotspot - truncate_cpy(netApctlInfo.name, sizeof(netApctlInfo.name), APname.c_str()); - truncate_cpy(netApctlInfo.ssid, sizeof(netApctlInfo.ssid), APname.c_str()); - memcpy(netApctlInfo.bssid, "\1\1\2\2\3\3", sizeof(netApctlInfo.bssid)); // fake AP's mac address - netApctlInfo.ssidLength = static_cast(APname.length()); - truncate_cpy(netApctlInfo.ip, sizeof(netApctlInfo.ip), "0.0.0.0"); - truncate_cpy(netApctlInfo.gateway, sizeof(netApctlInfo.gateway), "0.0.0.0"); - truncate_cpy(netApctlInfo.primaryDns, sizeof(netApctlInfo.primaryDns), "0.0.0.0"); - truncate_cpy(netApctlInfo.secondaryDns, sizeof(netApctlInfo.secondaryDns), "0.0.0.0"); - truncate_cpy(netApctlInfo.subNetMask, sizeof(netApctlInfo.subNetMask), "0.0.0.0"); + NetApctl_InitDefaultInfo(); // Create APctl fake-Thread netValidateLoopMemory(); @@ -863,12 +1957,35 @@ static int sceNetApctlInit(int stackSize, int initPriority) { __KernelStartThread(apctlThreadID, 0, 0); } + // Note: Borrowing AdhocServer for Grouping purpose + u32 structsz = sizeof(SceNetAdhocctlAdhocId); + if (apctlProdCodeAddr != 0) { + userMemory.Free(apctlProdCodeAddr); + } + apctlProdCodeAddr = userMemory.Alloc(structsz, false, "ApctlAdhocId"); + SceNetAdhocctlAdhocId* prodCode = (SceNetAdhocctlAdhocId*)Memory::GetCharPointer(apctlProdCodeAddr); + if (prodCode) { + memset(prodCode, 0, structsz); + // TODO: Use a 9-characters product id instead of disc id (ie. not null-terminated(VT_UTF8_SPE) and shouldn't be variable size?) + std::string discID = g_paramSFO.GetDiscID(); + prodCode->type = 1; // VT_UTF8 since we're using DiscID instead of product id + memcpy(prodCode->data, discID.c_str(), std::min(ADHOCCTL_ADHOCID_LEN, (int)discID.size())); + } + sceNetAdhocctlInit(stackSize, initPriority, apctlProdCodeAddr); + netApctlInited = true; return 0; } int NetApctl_Term() { + // Note: Since we're borrowing AdhocServer for Grouping purpose, we should cleanup too + NetAdhocctl_Term(); + if (apctlProdCodeAddr != 0) { + userMemory.Free(apctlProdCodeAddr); + apctlProdCodeAddr = 0; + } + // Cleanup Apctl resources // Delete fake PSP Thread if (apctlThreadID != 0) { @@ -885,14 +2002,21 @@ int NetApctl_Term() { int sceNetApctlTerm() { WARN_LOG(SCENET, "UNTESTED %s()", __FUNCTION__); - return NetApctl_Term(); + int retval = NetApctl_Term(); + + hleEatMicro(adhocDefaultDelay); + return retval; } static int sceNetApctlGetInfo(int code, u32 pInfoAddr) { - WARN_LOG(SCENET, "UNTESTED %s(%i, %08x)", __FUNCTION__, code, pInfoAddr); + DEBUG_LOG(SCENET, "UNTESTED %s(%i, %08x) at %08x", __FUNCTION__, code, pInfoAddr, currentMIPS->pc); + + // FIXME: Driver 76 need to use sceNetApctlGetInfo without initializing/connecting Apctl, but they use Adhocctl instead, so may be sceNetApctlGetInfo also affected by Adhocctl? + //if (!netApctlInited) + // return hleLogError(SCENET, ERROR_NET_APCTL_NOT_INITIALIZED, "apctl not initialized?"); // FIXME: Official prx files seems to return 0x80410a0d when Apctl not initialized yet? - if (!netApctlInited) - return hleLogError(SCENET, ERROR_NET_APCTL_NOT_IN_BSS, "apctl not in bss"); // Only have valid info after joining an AP and got an IP, right? + //if (netApctlState != PSP_NET_APCTL_STATE_GOT_IP) + // return hleLogError(SCENET, ERROR_NET_APCTL_NOT_IN_BSS, "apctl not connected?"); // FIXME: Official prx files seems to return 0x80410a05 when Apctl not connected yet? switch (code) { case PSP_NET_APCTL_INFO_PROFILE_NAME: @@ -1050,7 +2174,7 @@ int NetApctl_AddHandler(u32 handlerPtr, u32 handlerArg) { return retval; } apctlHandlers[retval] = handler; - WARN_LOG(SCENET, "Added Apctl handler(%x, %x): %d", handlerPtr, handlerArg, retval); + DEBUG_LOG(SCENET, "Added Apctl handler(%x, %x): %d", handlerPtr, handlerArg, retval); } else { ERROR_LOG(SCENET, "Existing Apctl handler(%x, %x)", handlerPtr, handlerArg); @@ -1083,121 +2207,980 @@ static int sceNetApctlDelHandler(u32 handlerID) { return NetApctl_DelHandler(handlerID); } -static int sceNetInetInetAton(const char *hostname, u32 addrPtr) { - ERROR_LOG(SCENET, "UNIMPL sceNetInetInetAton(%s, %08x)", hostname, addrPtr); - return -1; +static int sceNetInetInetPton(int af, const char* hostname, u32 inAddrPtr) { + WARN_LOG(SCENET, "UNTESTED sceNetInetInetPton(%i, %s, %08x)", af, safe_string(hostname), inAddrPtr); + if (!Memory::IsValidAddress(inAddrPtr)) { + return hleLogError(SCENET, 0, "invalid arg"); //-1 + } + + int retval = inet_pton(convertSocketDomainPSP2Host(af), hostname, (void*)Memory::GetPointer(inAddrPtr)); + return hleLogSuccessI(SCENET, retval); +} + +static int sceNetInetInetAton(const char* hostname, u32 inAddrPtr) { + WARN_LOG(SCENET, "UNTESTED sceNetInetInetAton(%s, %08x)", safe_string(hostname), inAddrPtr); + if (!Memory::IsValidAddress(inAddrPtr)) { + return hleLogError(SCENET, 0, "invalid arg"); //-1 + } + + int retval = inet_pton(AF_INET, hostname, (void*)Memory::GetPointer(inAddrPtr)); + // inet_aton() returns nonzero if the address is valid, zero if not. + return hleLogSuccessI(SCENET, retval); +} + +// TODO: Need to find out whether it's possible to get partial output or not, since Coded Arms Contagion is using a small bufsize(4) +static u32 sceNetInetInetNtop(int af, u32 srcInAddrPtr, u32 dstBufPtr, u32 bufsize) { + WARN_LOG(SCENET, "UNTESTED sceNetInetInetNtop(%i, %08x, %08x, %d)", af, srcInAddrPtr, dstBufPtr, bufsize); + if (!Memory::IsValidAddress(srcInAddrPtr)) { + return hleLogError(SCENET, 0, "invalid arg"); + } + if (!Memory::IsValidAddress(dstBufPtr) || bufsize < 1/*8*/) { // usually 8 or 16, but Coded Arms Contagion is using bufsize = 4 + inetLastErrno = ENOSPC; + return hleLogError(SCENET, 0, "invalid arg"); + } + + if (inet_ntop(convertSocketDomainPSP2Host(af), Memory::GetCharPointer(srcInAddrPtr), (char*)Memory::GetCharPointer(dstBufPtr), bufsize) == NULL) { + //return hleLogDebug(SCENET, 0, "invalid arg?"); // Temporarily commented out in case it's allowed to have partial output + } + return hleLogSuccessX(SCENET, dstBufPtr, "%s", safe_string(Memory::GetCharPointer(dstBufPtr))); +} + +static u32_le sceNetInetInetAddr(const char* hostname) { + WARN_LOG(SCENET, "UNTESTED sceNetInetInetAddr(%s)", safe_string(hostname)); + if (hostname == nullptr || hostname[0]=='\0') + return hleLogError(SCENET, INADDR_NONE, "invalid arg"); + + u32 retval = INADDR_NONE; // inet_addr(hostname); // deprecated? + inet_pton(AF_INET, hostname, &retval); // Alternative to the deprecated inet_addr + + return hleLogSuccessX(SCENET, retval); +} + +static int sceNetInetGetpeername(int sock, u32 namePtr, u32 namelenPtr) { + WARN_LOG(SCENET, "UNTESTED %s(%i, %08x, %08x)", __FUNCTION__, sock, namePtr, namelenPtr); + if (!Memory::IsValidAddress(namePtr) || !Memory::IsValidAddress(namelenPtr)) { + inetLastErrno = EFAULT; + return hleLogError(SCENET, -1, "invalid arg"); + } + + SceNetInetSockaddr* name = (SceNetInetSockaddr*)Memory::GetPointer(namePtr); + int* namelen = (int*)Memory::GetPointer(namelenPtr); + SockAddrIN4 saddr{}; + // TODO: Should've created convertSockaddrPSP2Host (and Host2PSP too) function as it's being used pretty often, thus fixing a bug on it will be tedious when scattered all over the places + saddr.addr.sa_family = name->sa_family; + int len = std::min(*namelen > 0 ? *namelen : 0, static_cast(sizeof(saddr))); + memcpy(saddr.addr.sa_data, name->sa_data, sizeof(name->sa_data)); + int retval = getpeername(sock, (sockaddr*)&saddr, (socklen_t*)&len); + DEBUG_LOG(SCENET, "Getpeername: Family = %s, Address = %s, Port = %d", inetSocketDomain2str(saddr.addr.sa_family).c_str(), ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + *namelen = len; + if (retval < 0) { + inetLastErrno = errno; + return hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + } + else { + memcpy(name->sa_data, saddr.addr.sa_data, len-(sizeof(name->sa_len)+sizeof(name->sa_family))); + name->sa_len = len; + name->sa_family = saddr.addr.sa_family; + } + return 0; } -int sceNetInetPoll(void *fds, u32 nfds, int timeout) { // timeout in miliseconds - DEBUG_LOG(SCENET, "UNTESTED sceNetInetPoll(%p, %d, %i) at %08x", fds, nfds, timeout, currentMIPS->pc); +static int sceNetInetGetsockname(int sock, u32 namePtr, u32 namelenPtr) { + WARN_LOG(SCENET, "UNTESTED %s(%i, %08x, %08x)", __FUNCTION__, sock, namePtr, namelenPtr); + if (!Memory::IsValidAddress(namePtr) || !Memory::IsValidAddress(namelenPtr)) { + inetLastErrno = EFAULT; + return hleLogError(SCENET, -1, "invalid arg"); + } + + SceNetInetSockaddr* name = (SceNetInetSockaddr*)Memory::GetPointer(namePtr); + int* namelen = (int*)Memory::GetPointer(namelenPtr); + SockAddrIN4 saddr{}; + saddr.addr.sa_family = name->sa_family; + int len = std::min(*namelen > 0 ? *namelen : 0, static_cast(sizeof(saddr))); + memcpy(saddr.addr.sa_data, name->sa_data, sizeof(name->sa_data)); + int retval = getsockname(sock, (sockaddr*)&saddr, (socklen_t*)&len); + DEBUG_LOG(SCENET, "Getsockname: Family = %s, Address = %s, Port = %d", inetSocketDomain2str(saddr.addr.sa_family).c_str(), ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + *namelen = len; + if (retval < 0) { + inetLastErrno = errno; + return hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + } + else { + memcpy(name->sa_data, saddr.addr.sa_data, len - (sizeof(name->sa_len) + sizeof(name->sa_family))); + name->sa_len = len; + name->sa_family = saddr.addr.sa_family; + } + return 0; +} + +// FIXME: nfds is number of fd(s) as in posix poll, or was it maximum fd value as in posix select? Star Wars Battlefront Renegade seems to set the nfds to 64, while Coded Arms Contagion is using 256 +int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr, u32 timeoutPtr) { + DEBUG_LOG(SCENET, "UNTESTED sceNetInetSelect(%i, %08x, %08x, %08x, %08x) at %08x", nfds, readfdsPtr, writefdsPtr, exceptfdsPtr, timeoutPtr, currentMIPS->pc); int retval = -1; - SceNetInetPollfd *fdarray = (SceNetInetPollfd *)fds; // SceNetInetPollfd/pollfd, sceNetInetPoll() have similarity to BSD poll() but pollfd have different size on 64bit -//#ifdef _WIN32 - //WSAPoll only available for Vista or newer, so we'll use an alternative way for XP since Windows doesn't have poll function like *NIX - if (nfds > FD_SETSIZE) return -1; - fd_set readfds, writefds, exceptfds; + SceNetInetFdSet* readfds = (SceNetInetFdSet*)Memory::GetCharPointer(readfdsPtr); + SceNetInetFdSet* writefds = (SceNetInetFdSet*)Memory::GetCharPointer(writefdsPtr); + SceNetInetFdSet* exceptfds = (SceNetInetFdSet*)Memory::GetCharPointer(exceptfdsPtr); + SceNetInetTimeval* timeout = (SceNetInetTimeval*)Memory::GetCharPointer(timeoutPtr); + // TODO: Use poll instead of select since Windows' FD_SETSIZE is only 64 while PSP is 256, and select can only work for fd value less than FD_SETSIZE on some system + fd_set rdfds{}, wrfds{}, exfds{}; + FD_ZERO(&rdfds); FD_ZERO(&wrfds); FD_ZERO(&exfds); + int maxfd = nfds; // (nfds > PSP_NET_INET_FD_SETSIZE) ? nfds : PSP_NET_INET_FD_SETSIZE; + int rdcnt = 0, wrcnt = 0, excnt = 0; + for (int i = maxfd - 1; i >= 0 /*&& i >= maxfd - 64*/; i--) { + bool windows_workaround = false; +#if PPSSPP_PLATFORM(WINDOWS) + //windows_workaround = (i == nfds - 1); +#endif + if (readfds != NULL && (NetInetFD_ISSET(i, readfds) || windows_workaround)) { + VERBOSE_LOG(SCENET, "Input Read FD #%i", i); + if (rdcnt < FD_SETSIZE) { + FD_SET(i, &rdfds); // This might pointed to a non-existing socket or sockets belonged to other programs on Windows, because most of the time Windows socket have an id above 1k instead of 0-255 + rdcnt++; + } + } + if (writefds != NULL && (NetInetFD_ISSET(i, writefds) || windows_workaround)) { + VERBOSE_LOG(SCENET, "Input Write FD #%i", i); + if (wrcnt < FD_SETSIZE) { + FD_SET(i, &wrfds); + wrcnt++; + } + } + if (exceptfds != NULL && (NetInetFD_ISSET(i, exceptfds) || windows_workaround)) { + VERBOSE_LOG(SCENET, "Input Except FD #%i", i); + if (excnt < FD_SETSIZE) { + FD_SET(i, &exfds); + excnt++; + } + } + } + // Workaround for games that set ndfs to 64 instead of socket id + 1 + if (inetLastSocket >= 0) { + if (readfds != NULL && rdcnt == 0) { + FD_SET(inetLastSocket, &rdfds); + rdcnt++; + } + if (writefds != NULL && wrcnt == 0) { + FD_SET(inetLastSocket, &wrfds); + wrcnt++; + } + if (exceptfds != NULL && excnt == 0) { + FD_SET(inetLastSocket, &exfds); + excnt++; + } + } + + timeval tmout = {5, 543210}; // Workaround timeout value when timeout = NULL + if (timeout != NULL) { + tmout.tv_sec = timeout->tv_sec; + tmout.tv_usec = timeout->tv_usec; + } + VERBOSE_LOG(SCENET, "Select: Read count: %d, Write count: %d, Except count: %d, TimeVal: %u.%u", rdcnt, wrcnt, excnt, tmout.tv_sec, tmout.tv_usec); + // TODO: Simulate blocking behaviour when timeout = NULL to prevent PPSSPP from freezing + retval = select(nfds, (readfds == NULL) ? NULL: &rdfds, (writefds == NULL) ? NULL: &wrfds, (exceptfds == NULL) ? NULL: &exfds, /*(timeout == NULL) ? NULL :*/ &tmout); + if (readfds != NULL && inetLastSocket < maxfd) NetInetFD_ZERO(readfds); // Clear it only when not needing a workaround + if (writefds != NULL && inetLastSocket < maxfd) NetInetFD_ZERO(writefds); // Clear it only when not needing a workaround + if (exceptfds != NULL) NetInetFD_ZERO(exceptfds); + for (int i = maxfd - 1; i >= 0 /*&& i >= maxfd - 64*/; i--) { + if (readfds != NULL && FD_ISSET(i, &rdfds)) + NetInetFD_SET(i, readfds); + if (writefds != NULL && FD_ISSET(i, &wrfds)) + NetInetFD_SET(i, writefds); + if (exceptfds != NULL && FD_ISSET(i, &exfds)) + NetInetFD_SET(i, exceptfds); + } + // Workaround for games that set ndfs to 64 instead of socket id + 1 + if (inetLastSocket >= 0) { + if (readfds != NULL && rdcnt == 1 && FD_ISSET(inetLastSocket, &rdfds)) + NetInetFD_SET(inetLastSocket, readfds); + if (writefds != NULL && wrcnt == 1 && FD_ISSET(inetLastSocket, &wrfds)) + NetInetFD_SET(inetLastSocket, writefds); + if (exceptfds != NULL && excnt == 1 && FD_ISSET(inetLastSocket, &exfds)) + NetInetFD_SET(inetLastSocket, exceptfds); + } + + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == 0) + hleLogDebug(SCENET, retval, "errno = %d", inetLastErrno); + else if (inetLastErrno < 0) + hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + return hleLogDebug(SCENET, hleDelayResult(retval, "workaround until blocking-socket", 500)); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented (ie. Coded Arms Contagion) + } + return hleLogSuccessI(SCENET, hleDelayResult(retval, "workaround until blocking-socket", 500)); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented (ie. Coded Arms Contagion) +} + +int sceNetInetPoll(u32 fdsPtr, u32 nfds, int timeout) { // timeout in miliseconds just like posix poll? or in microseconds as other PSP timeout? + DEBUG_LOG(SCENET, "UNTESTED sceNetInetPoll(%08x, %d, %i) at %08x", fdsPtr, nfds, timeout, currentMIPS->pc); + int retval = -1; + int maxfd = 0; + SceNetInetPollfd *fdarray = (SceNetInetPollfd*)Memory::GetPointer(fdsPtr); // SceNetInetPollfd/pollfd, sceNetInetPoll() have similarity to BSD poll() but pollfd have different size on 64bit + + if (nfds > FD_SETSIZE) + nfds = FD_SETSIZE; + + fd_set readfds{}, writefds{}, exceptfds{}; FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); for (int i = 0; i < (s32)nfds; i++) { - if (fdarray[i].events & (INET_POLLRDNORM)) FD_SET(fdarray[i].fd, &readfds); // (POLLRDNORM | POLLIN) - if (fdarray[i].events & (INET_POLLWRNORM)) FD_SET(fdarray[i].fd, &writefds); // (POLLWRNORM | POLLOUT) - //if (fdarray[i].events & (ADHOC_EV_ALERT)) // (POLLRDBAND | POLLPRI) // POLLERR + if (fdarray[i].fd < 0) { + inetLastErrno = EINVAL; + return hleLogError(SCENET, -1, "invalid socket id"); + } + if (fdarray[i].fd > maxfd) maxfd = fdarray[i].fd; + FD_SET(fdarray[i].fd, &readfds); + FD_SET(fdarray[i].fd, &writefds); FD_SET(fdarray[i].fd, &exceptfds); fdarray[i].revents = 0; } - timeval tmout; - tmout.tv_sec = timeout / 1000; // seconds - tmout.tv_usec = (timeout % 1000) * 1000; // microseconds - retval = select(nfds, &readfds, &writefds, &exceptfds, &tmout); - if (retval < 0) return -1; - retval = 0; - for (int i = 0; i < (s32)nfds; i++) { - if (FD_ISSET(fdarray[i].fd, &readfds)) fdarray[i].revents |= INET_POLLRDNORM; //POLLIN - if (FD_ISSET(fdarray[i].fd, &writefds)) fdarray[i].revents |= INET_POLLWRNORM; //POLLOUT - fdarray[i].revents &= fdarray[i].events; - if (FD_ISSET(fdarray[i].fd, &exceptfds)) fdarray[i].revents |= ADHOC_EV_ALERT; // POLLPRI; // POLLERR; // can be raised on revents regardless of events bitmask? - if (fdarray[i].revents) retval++; - } -//#else - /* - // Doesn't work properly yet - pollfd *fdtmp = (pollfd *)malloc(sizeof(pollfd) * nfds); - // Note: sizeof(pollfd) = 16bytes in 64bit and 8bytes in 32bit, while sizeof(SceNetInetPollfd) is always 8bytes - for (int i = 0; i < (s32)nfds; i++) { - fdtmp[i].fd = fdarray[i].fd; - fdtmp[i].events = 0; - if (fdarray[i].events & INET_POLLRDNORM) fdtmp[i].events |= (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI); - if (fdarray[i].events & INET_POLLWRNORM) fdtmp[i].events |= (POLLOUT | POLLWRNORM | POLLWRBAND); - fdtmp[i].revents = 0; - fdarray[i].revents = 0; + timeval tmout = { 5, 543210 }; // Workaround timeout value when timeout = NULL + if (timeout >= 0) { + tmout.tv_sec = timeout / 1000000; // seconds + tmout.tv_usec = (timeout % 1000000); // microseconds } - retval = poll(fdtmp, (nfds_t)nfds, timeout); //retval = WSAPoll(fdarray, nfds, timeout); + // TODO: Simulate blocking behaviour when timeout is non-zero to prevent PPSSPP from freezing + retval = select(maxfd + 1, &readfds, &writefds, &exceptfds, /*(timeout<0)? NULL:*/&tmout); + if (retval < 0) { + inetLastErrno = EINTR; + return hleLogError(SCENET, hleDelayResult(retval, "workaround until blocking-socket", 500)); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented + } + + retval = 0; for (int i = 0; i < (s32)nfds; i++) { - if (fdtmp[i].revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) fdarray[i].revents |= INET_POLLRDNORM; - if (fdtmp[i].revents & (POLLOUT | POLLWRNORM | POLLWRBAND)) fdarray[i].revents |= INET_POLLWRNORM; + if ((fdarray[i].events & (INET_POLLRDNORM | INET_POLLIN)) && FD_ISSET(fdarray[i].fd, &readfds)) + fdarray[i].revents |= (INET_POLLRDNORM | INET_POLLIN); //POLLIN_SET + if ((fdarray[i].events & (INET_POLLWRNORM | INET_POLLOUT)) && FD_ISSET(fdarray[i].fd, &writefds)) + fdarray[i].revents |= (INET_POLLWRNORM | INET_POLLOUT); //POLLOUT_SET fdarray[i].revents &= fdarray[i].events; - if (fdtmp[i].revents & POLLERR) fdarray[i].revents |= POLLERR; //INET_POLLERR // can be raised on revents regardless of events bitmask? + if (FD_ISSET(fdarray[i].fd, &exceptfds)) + fdarray[i].revents |= (INET_POLLRDBAND | INET_POLLPRI | INET_POLLERR); //POLLEX_SET; // Can be raised on revents regardless of events bitmask? + if (fdarray[i].revents) + retval++; + VERBOSE_LOG(SCENET, "Poll Socket#%d Fd: %d, events: %04x, revents: %04x, availToRecv: %d", i, fdarray[i].fd, fdarray[i].events, fdarray[i].revents, getAvailToRecv(fdarray[i].fd)); } - free(fdtmp); - */ -//#endif - return retval; + //hleEatMicro(1000); + return hleLogSuccessI(SCENET, hleDelayResult(retval, "workaround until blocking-socket", 1000)); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented } static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, u32 flags) { - ERROR_LOG(SCENET, "UNIMPL sceNetInetRecv(%i, %08x, %i, %08x)", socket, bufPtr, bufLen, flags); - return -1; + DEBUG_LOG(SCENET, "UNTESTED sceNetInetRecv(%i, %08x, %i, %08x) at %08x", socket, bufPtr, bufLen, flags, currentMIPS->pc); + int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode + flgs = convertMSGFlagsPSP2Host(flgs); + int retval = recv(socket, (char*)Memory::GetPointer(bufPtr), bufLen, flgs | MSG_NOSIGNAL); + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == EAGAIN) + hleLogDebug(SCENET, retval, "errno = %d", inetLastErrno); + else + hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + return hleDelayResult(retval, "workaround until blocking-socket", 500); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented + } + + std::string datahex; + DataToHexString(10, 0, Memory::GetPointer(bufPtr), retval, &datahex); + VERBOSE_LOG(SCENET, "Data Dump (%d bytes):\n%s", retval, datahex.c_str()); + + return hleLogSuccessInfoI(SCENET, hleDelayResult(retval, "workaround until blocking-socket", 500)); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented } static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) { - ERROR_LOG(SCENET, "UNIMPL sceNetInetSend(%i, %08x, %i, %08x)", socket, bufPtr, bufLen, flags); - return -1; -} + DEBUG_LOG(SCENET, "UNTESTED sceNetInetSend(%i, %08x, %i, %08x) at %08x", socket, bufPtr, bufLen, flags, currentMIPS->pc); -static int sceNetInetGetErrno() { - ERROR_LOG(SCENET, "UNTESTED sceNetInetGetErrno()"); - int error = errno; - switch (error) { - case ETIMEDOUT: - return INET_ETIMEDOUT; - case EISCONN: - return INET_EISCONN; - case EINPROGRESS: - return INET_EINPROGRESS; - //case EAGAIN: - // return INET_EAGAIN; + std::string datahex; + DataToHexString(10, 0, Memory::GetPointer(bufPtr), bufLen, &datahex); + VERBOSE_LOG(SCENET, "Data Dump (%d bytes):\n%s", bufLen, datahex.c_str()); + + int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode + flgs = convertMSGFlagsPSP2Host(flgs); + int retval = send(socket, (char*)Memory::GetPointer(bufPtr), bufLen, flgs | MSG_NOSIGNAL); + + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == EAGAIN) + hleLogDebug(SCENET, retval, "errno = %d", inetLastErrno); + else + hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + return retval; } - return error; //-1; + + return hleLogSuccessInfoI(SCENET, retval); } static int sceNetInetSocket(int domain, int type, int protocol) { - ERROR_LOG(SCENET, "UNIMPL sceNetInetSocket(%i, %i, %i)", domain, type, protocol); - return -1; + WARN_LOG(SCENET, "UNTESTED sceNetInetSocket(%i, %i, %i) at %08x", domain, type, protocol, currentMIPS->pc); + DEBUG_LOG(SCENET, "Socket: Domain = %s, Type = %s, Protocol = %s", inetSocketDomain2str(domain).c_str(), inetSocketType2str(type).c_str(), inetSocketProto2str(protocol).c_str()); + int retval = socket(convertSocketDomainPSP2Host(domain), convertSocketTypePSP2Host(type), convertSocketProtoPSP2Host(protocol)); + if (retval < 0) { + inetLastErrno = errno; + return hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + } + + //InetSocket* sock = new InetSocket(domain, type, protocol); + //retval = pspSockets.Create(sock); + + // Ignore SIGPIPE when supported (ie. BSD/MacOS) + setSockNoSIGPIPE(retval, 1); + // TODO: We should always use non-blocking mode and simulate blocking mode + changeBlockingMode(retval, 1); + // Enable Port Re-use, required for multiple-instance + setSockReuseAddrPort(retval); + // Disable Connection Reset error on UDP to avoid strange behavior + setUDPConnReset(retval, false); + + inetLastSocket = retval; + return hleLogSuccessI(SCENET, retval); } static int sceNetInetSetsockopt(int socket, int level, int optname, u32 optvalPtr, int optlen) { - ERROR_LOG(SCENET, "UNIMPL sceNetInetSetsockopt(%i, %i, %i, %08x, %i)", socket, level, optname, optvalPtr, optlen); - return -1; + WARN_LOG(SCENET, "UNTESTED %s(%i, %i, %i, %08x, %i) at %08x", __FUNCTION__, socket, level, optname, optvalPtr, optlen, currentMIPS->pc); + u32_le* optval = (u32_le*)Memory::GetPointer(optvalPtr); + DEBUG_LOG(SCENET, "SockOpt: Level = %s, OptName = %s, OptValue = %d", inetSockoptLevel2str(level).c_str(), inetSockoptName2str(optname, level).c_str(), *optval); + timeval tval{}; + // InetSocket* sock = pspSockets.Get(socket, error); + // TODO: Ignoring SO_NBIO/SO_NONBLOCK flag if we always use non-bloking mode (ie. simulated blocking mode) + if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_NBIO) { + //memcpy(&sock->nonblocking, (int*)optval, std::min(sizeof(sock->nonblocking), optlen)); + return hleLogSuccessI(SCENET, 0); + } + // FIXME: Should we ignore SO_BROADCAST flag since we are using fake broadcast (ie. only broadcast to friends), + // But Infrastructure/Online play might need to use broadcast for SSDP and to support LAN MP with real PSP + /*else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_BROADCAST) { + //memcpy(&sock->so_broadcast, (int*)optval, std::min(sizeof(sock->so_broadcast), optlen)); + return hleLogSuccessI(SCENET, 0); + }*/ + // TODO: Ignoring SO_REUSEADDR flag to prevent disrupting multiple-instance feature + else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_REUSEADDR) { + //memcpy(&sock->reuseaddr, (int*)optval, std::min(sizeof(sock->reuseaddr), optlen)); + return hleLogSuccessI(SCENET, 0); + } + // TODO: Ignoring SO_REUSEPORT flag to prevent disrupting multiple-instance feature (not sure if PSP has SO_REUSEPORT or not tho, defined as 15 on Android) + else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_REUSEPORT) { // 15 + //memcpy(&sock->reuseport, (int*)optval, std::min(sizeof(sock->reuseport), optlen)); + return hleLogSuccessI(SCENET, 0); + } + // TODO: Ignoring SO_NOSIGPIPE flag to prevent crashing PPSSPP (not sure if PSP has NOSIGPIPE or not tho, defined as 0x1022 on Darwin) + else if (level == PSP_NET_INET_SOL_SOCKET && optname == 0x1022) { // PSP_NET_INET_SO_NOSIGPIPE ? + //memcpy(&sock->nosigpipe, (int*)optval, std::min(sizeof(sock->nosigpipe), optlen)); + return hleLogSuccessI(SCENET, 0); + } + // It seems UNO game will try to set socket buffer size with a very large size and ended getting error (-1), so we should also limit the buffer size to replicate PSP behavior + else if (level == PSP_NET_INET_SOL_SOCKET && (optname == PSP_NET_INET_SO_RCVBUF || optname == PSP_NET_INET_SO_SNDBUF)) { // PSP_NET_INET_SO_NOSIGPIPE ? + // TODO: For SOCK_STREAM max buffer size is 8 Mb on BSD, while max SOCK_DGRAM is 65535 minus the IP & UDP Header size + if (*optval > 8 * 1024 * 1024) { + inetLastErrno = ENOBUFS; // FIXME: return ENOBUFS for SOCK_STREAM, and EINVAL for SOCK_DGRAM + return hleLogError(SCENET, -1, "buffer size too large?"); + } + } + int retval = 0; + // PSP timeout are a single 32bit value (micro seconds) + if (level == PSP_NET_INET_SOL_SOCKET && optval && (optname == PSP_NET_INET_SO_RCVTIMEO || optname == PSP_NET_INET_SO_SNDTIMEO)) { + tval.tv_sec = *optval / 1000000; // seconds + tval.tv_usec = (*optval % 1000000); // microseconds + retval = setsockopt(socket, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)&tval, sizeof(tval)); + } + else { + retval = setsockopt(socket, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)optval, optlen); + } + if (retval < 0) { + inetLastErrno = errno; + hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + } + return hleLogSuccessI(SCENET, retval); +} + +static int sceNetInetGetsockopt(int socket, int level, int optname, u32 optvalPtr, u32 optlenPtr) { + WARN_LOG(SCENET, "UNTESTED %s(%i, %i, %i, %08x, %08x) at %08x", __FUNCTION__, socket, level, optname, optvalPtr, optlenPtr, currentMIPS->pc); + u32_le* optval = (u32_le*)Memory::GetPointer(optvalPtr); + socklen_t* optlen = (socklen_t*)Memory::GetPointer(optlenPtr); + DEBUG_LOG(SCENET, "SockOpt: Level = %s, OptName = %s", inetSockoptLevel2str(level).c_str(), inetSockoptName2str(optname, level).c_str()); + timeval tval{}; + // InetSocket* sock = pspSockets.Get(socket, error); + // TODO: Ignoring SO_NBIO/SO_NONBLOCK flag if we always use non-bloking mode (ie. simulated blocking mode) + if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_NBIO) { + //*optlen = std::min(sizeof(sock->nonblocking), *optlen); + //memcpy((int*)optval, &sock->nonblocking, *optlen); + //if (sock->nonblocking && *optlen>0) *optval = 0x80; // on true, returning 0x80 when retrieved using getsockopt? + return hleLogSuccessI(SCENET, 0); + } + // FIXME: Should we ignore SO_BROADCAST flag since we are using fake broadcast (ie. only broadcast to friends), + // But Infrastructure/Online play might need to use broadcast for SSDP and to support LAN MP with real PSP + /*else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_BROADCAST) { + //*optlen = std::min(sizeof(sock->so_broadcast), *optlen); + //memcpy((int*)optval, &sock->so_broadcast, *optlen); + //if (sock->so_broadcast && *optlen>0) *optval = 0x80; // on true, returning 0x80 when retrieved using getsockopt? + return hleLogSuccessI(SCENET, 0); + }*/ + // TODO: Ignoring SO_REUSEADDR flag to prevent disrupting multiple-instance feature + else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_REUSEADDR) { + //*optlen = std::min(sizeof(sock->reuseaddr), *optlen); + //memcpy((int*)optval, &sock->reuseaddr, *optlen); + return hleLogSuccessI(SCENET, 0); + } + // TODO: Ignoring SO_REUSEPORT flag to prevent disrupting multiple-instance feature (not sure if PSP has SO_REUSEPORT or not tho, defined as 15 on Android) + else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_REUSEPORT) { // 15 + //*optlen = std::min(sizeof(sock->reuseport), *optlen); + //memcpy((int*)optval, &sock->reuseport, *optlen); + return hleLogSuccessI(SCENET, 0); + } + // TODO: Ignoring SO_NOSIGPIPE flag to prevent crashing PPSSPP (not sure if PSP has NOSIGPIPE or not tho, defined as 0x1022 on Darwin) + else if (level == PSP_NET_INET_SOL_SOCKET && optname == 0x1022) { // PSP_NET_INET_SO_NOSIGPIPE ? + //*optlen = std::min(sizeof(sock->nosigpipe), *optlen); + //memcpy((int*)optval, &sock->nosigpipe, *optlen); + return hleLogSuccessI(SCENET, 0); + } + int retval = 0; + // PSP timeout are a single 32bit value (micro seconds) + if (level == PSP_NET_INET_SOL_SOCKET && optval && (optname == PSP_NET_INET_SO_RCVTIMEO || optname == PSP_NET_INET_SO_SNDTIMEO)) { + socklen_t tvlen = sizeof(tval); + retval = getsockopt(socket, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)&tval, &tvlen); + if (retval != SOCKET_ERROR) { + u64_le val = (tval.tv_sec * 1000000LL) + tval.tv_usec; + memcpy(optval, &val, std::min(static_cast(sizeof(val)), std::min(static_cast(sizeof(*optval)), *optlen))); + } + } + else { + retval = getsockopt(socket, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)optval, optlen); + } + if (retval < 0) { + inetLastErrno = errno; + hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + } + DEBUG_LOG(SCENET, "SockOpt: OptValue = %d", *optval); + return hleLogSuccessI(SCENET, retval); +} + +static int sceNetInetBind(int socket, u32 namePtr, int namelen) { + WARN_LOG(SCENET, "UNTESTED %s(%i, %08x, %i) at %08x", __FUNCTION__, socket, namePtr, namelen, currentMIPS->pc); + SceNetInetSockaddr* name = (SceNetInetSockaddr*)Memory::GetPointer(namePtr); + SockAddrIN4 saddr{}; + // TODO: Should've created convertSockaddrPSP2Host (and Host2PSP too) function as it's being used pretty often, thus fixing a bug on it will be tedious when scattered all over the places + saddr.addr.sa_family = name->sa_family; + int len = std::min(namelen > 0 ? namelen : 0, static_cast(sizeof(saddr))); + memcpy(saddr.addr.sa_data, name->sa_data, sizeof(name->sa_data)); + if (isLocalServer) { + getLocalIp(&saddr.in); + } + // FIXME: On non-Windows broadcast to INADDR_BROADCAST(255.255.255.255) might not be received by the sender itself when binded to specific IP (ie. 192.168.0.2) or INADDR_BROADCAST. + // Meanwhile, it might be received by itself when binded to subnet (ie. 192.168.0.255) or INADDR_ANY(0.0.0.0). + if (saddr.in.sin_addr.s_addr == INADDR_ANY || saddr.in.sin_addr.s_addr == INADDR_BROADCAST) { + // Replace INADDR_ANY with a specific IP in order not to send data through the wrong interface (especially during broadcast) + // Get Local IP Address + sockaddr_in sockAddr{}; + getLocalIp(&sockAddr); + DEBUG_LOG(SCENET, "Bind: Address Replacement = %s => %s", ip2str(saddr.in.sin_addr).c_str(), ip2str(sockAddr.sin_addr).c_str()); + saddr.in.sin_addr.s_addr = sockAddr.sin_addr.s_addr; + } + // TODO: Make use Port Offset only for PPSSPP to PPSSPP communications (ie. IP addresses available in the group/friendlist), otherwise should be considered as Online Service thus should use the port as is. + //saddr.in.sin_port = htons(ntohs(saddr.in.sin_port) + portOffset); + DEBUG_LOG(SCENET, "Bind: Family = %s, Address = %s, Port = %d", inetSocketDomain2str(saddr.addr.sa_family).c_str(), ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + changeBlockingMode(socket, 0); + int retval = bind(socket, (struct sockaddr*)&saddr, len); + if (retval < 0) { + inetLastErrno = errno; + changeBlockingMode(socket, 1); + return hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + } + changeBlockingMode(socket, 1); + // Update binded port number if it was 0 (any port) + memcpy(name->sa_data, saddr.addr.sa_data, sizeof(name->sa_data)); + // Enable Port-forwarding + // TODO: Check the socket type/protocol for SOCK_STREAM/SOCK_DGRAM or IPPROTO_TCP/IPPROTO_UDP instead of forwarding both protocol + // InetSocket* sock = pspSockets.Get(socket, error); + // UPnP_Add((sock->type == SOCK_STREAM)? IP_PROTOCOL_TCP: IP_PROTOCOL_UDP, port, port); + unsigned short port = ntohs(saddr.in.sin_port); + UPnP_Add(IP_PROTOCOL_UDP, port, port); + UPnP_Add(IP_PROTOCOL_TCP, port, port); + + // Workaround: Send a dummy 0 size message to AdhocServer IP to make sure the socket actually bound to an address when binded with INADDR_ANY before using getsockname, seems to fix sending DGRAM from incorrect port issue on Android + /*saddr.in.sin_addr.s_addr = g_adhocServerIP.in.sin_addr.s_addr; + saddr.in.sin_port = 0; + sendto(socket, dummyPeekBuf64k, 0, MSG_NOSIGNAL, (struct sockaddr*)&saddr, sizeof(saddr)); + */ + + return hleLogSuccessI(SCENET, retval); +} + +static int sceNetInetConnect(int socket, u32 sockAddrPtr, int sockAddrLen) { + WARN_LOG(SCENET, "UNTESTED %s(%i, %08x, %i) at %08x", __FUNCTION__, socket, sockAddrPtr, sockAddrLen, currentMIPS->pc); + SceNetInetSockaddr* dst = (SceNetInetSockaddr*)Memory::GetPointer(sockAddrPtr); + SockAddrIN4 saddr{}; + int dstlen = std::min(sockAddrLen > 0 ? sockAddrLen : 0, static_cast(sizeof(saddr))); + saddr.addr.sa_family = dst->sa_family; + memcpy(saddr.addr.sa_data, dst->sa_data, sizeof(dst->sa_data)); + DEBUG_LOG(SCENET, "Connect: Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + + // Workaround to avoid blocking for indefinitely + setSockTimeout(socket, SO_SNDTIMEO, 5000000); + setSockTimeout(socket, SO_RCVTIMEO, 5000000); + changeBlockingMode(socket, 0); // Use blocking mode as temporary fix for UNO, since we don't simulate blocking-mode yet + int retval = connect(socket, (struct sockaddr*)&saddr.addr, dstlen); + if (retval < 0) { + inetLastErrno = errno; + if (connectInProgress(inetLastErrno)) + hleLogDebug(SCENET, retval, "errno = %d", inetLastErrno); + else + hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + changeBlockingMode(socket, 1); + // TODO: Since we're temporarily forcing blocking-mode we'll need to change errno from ETIMEDOUT to EAGAIN + /*if (inetLastErrno == ETIMEDOUT) + inetLastErrno = EAGAIN; + */ + return hleLogDebug(SCENET, retval); + } + changeBlockingMode(socket, 1); + + return hleLogSuccessI(SCENET, retval); } -static int sceNetInetConnect(int socket, u32 sockAddrInternetPtr, int addressLength) { - ERROR_LOG(SCENET, "UNIMPL sceNetInetConnect(%i, %08x, %i)", socket, sockAddrInternetPtr, addressLength); - return -1; +static int sceNetInetListen(int socket, int backlog) { + WARN_LOG(SCENET, "UNTESTED %s(%i, %i) at %08x", __FUNCTION__, socket, backlog, currentMIPS->pc); + + int retval = listen(socket, (backlog == PSP_NET_INET_SOMAXCONN? SOMAXCONN : backlog)); + if (retval < 0) { + inetLastErrno = errno; + return hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + } + + return hleLogSuccessI(SCENET, retval); +} + +static int sceNetInetAccept(int socket, u32 addrPtr, u32 addrLenPtr) { + WARN_LOG(SCENET, "UNTESTED %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, addrPtr, addrLenPtr, currentMIPS->pc); + SceNetInetSockaddr* src = (SceNetInetSockaddr*)Memory::GetCharPointer(addrPtr); + socklen_t* srclen = (socklen_t*)Memory::GetCharPointer(addrLenPtr); + SockAddrIN4 saddr{}; + if (srclen) + *srclen = std::min((*srclen) > 0 ? *srclen : 0, static_cast(sizeof(saddr))); + int retval = accept(socket, (struct sockaddr*)&saddr.addr, srclen); + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == EAGAIN) + hleLogDebug(SCENET, retval, "errno = %d", inetLastErrno); + else + hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + return retval; + } + + if (src) { + src->sa_family = saddr.addr.sa_family; + memcpy(src->sa_data, saddr.addr.sa_data, sizeof(src->sa_data)); + src->sa_len = srclen? *srclen : 0; + } + DEBUG_LOG(SCENET, "Accept: Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + + return hleLogSuccessI(SCENET, retval); +} + +static int sceNetInetShutdown(int socket, int how) { + WARN_LOG(SCENET, "UNTESTED %s(%i, %i) at %08x", __FUNCTION__, socket, how, currentMIPS->pc); + // Convert HOW from PSP to Host + int hostHow = how; + switch (how) { + case PSP_NET_INET_SHUT_RD: hostHow = SHUT_RD; break; + case PSP_NET_INET_SHUT_WR: hostHow = SHUT_WR; break; + case PSP_NET_INET_SHUT_RDWR: hostHow = SHUT_RDWR; break; + } + return hleLogSuccessI(SCENET, shutdown(socket, hostHow)); +} + +static int sceNetInetSocketAbort(int socket) { + WARN_LOG(SCENET, "UNTESTED %s(%i)", __FUNCTION__, socket); + // FIXME: either using shutdown/close or select? probably using select if blocking mode is being simulated with non-blocking + return hleLogSuccessI(SCENET, shutdown(socket, SHUT_RDWR)); +} + +static int sceNetInetClose(int socket) { + WARN_LOG(SCENET, "UNTESTED %s(%i) at %08x", __FUNCTION__, socket, currentMIPS->pc); + return hleLogSuccessI(SCENET, closesocket(socket)); +} + +static int sceNetInetCloseWithRST(int socket) { + WARN_LOG(SCENET, "UNTESTED %s(%i) at %08x", __FUNCTION__, socket, currentMIPS->pc); + // Based on http://deepix.github.io/2016/10/21/tcprst.html + struct linger sl{}; + sl.l_onoff = 1; // non-zero value enables linger option in kernel + sl.l_linger = 0; // timeout interval in seconds + setsockopt(socket, SOL_SOCKET, SO_LINGER, (const char*)&sl, sizeof(sl)); + return hleLogSuccessI(SCENET, closesocket(socket)); +} + +static int sceNetInetRecvfrom(int socket, u32 bufferPtr, int len, int flags, u32 fromPtr, u32 fromlenPtr) { + DEBUG_LOG(SCENET, "UNTESTED %s(%i, %08x, %i, %08x, %08x, %08x) at %08x", __FUNCTION__, socket, bufferPtr, len, flags, fromPtr, fromlenPtr, currentMIPS->pc); + SceNetInetSockaddr* src = (SceNetInetSockaddr*)Memory::GetCharPointer(fromPtr); + socklen_t* srclen = (socklen_t*)Memory::GetCharPointer(fromlenPtr); + SockAddrIN4 saddr{}; + if (srclen) + *srclen = std::min((*srclen) > 0? *srclen: 0, static_cast(sizeof(saddr))); + int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode + flgs = convertMSGFlagsPSP2Host(flgs); + int retval = recvfrom(socket, (char*)Memory::GetPointer(bufferPtr), len, flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, srclen); + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == EAGAIN) + hleLogDebug(SCENET, retval, "errno = %d", inetLastErrno); + else + hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + return hleDelayResult(retval, "workaround until blocking-socket", 500); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented (ie. Coded Arms Contagion) + } + + if (src) { + src->sa_family = saddr.addr.sa_family; + memcpy(src->sa_data, saddr.addr.sa_data, sizeof(src->sa_data)); + src->sa_len = srclen ? *srclen : 0; + } + DEBUG_LOG(SCENET, "RecvFrom: Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + + // Discard if it came from APIPA address (ie. self-received broadcasts from 169.254.x.x when broadcasting to INADDR_BROADCAST on Windows) on Untold Legends The Warrior's Code / Twisted Metal Head On + /*if (isAPIPA(saddr.in.sin_addr.s_addr)) { + inetLastErrno = EAGAIN; + retval = -1; + DEBUG_LOG(SCENET, "RecvFrom: Ignoring Address = %s", ip2str(saddr.in.sin_addr).c_str()); + hleLogDebug(SCENET, retval, "faked errno = %d", inetLastErrno); + return hleDelayResult(retval, "workaround until blocking-socket", 500); + }*/ + + std::string datahex; + DataToHexString(0, 0, Memory::GetPointer(bufferPtr), retval, &datahex); + VERBOSE_LOG(SCENET, "Data Dump (%d bytes):\n%s", retval, datahex.c_str()); + + return hleLogSuccessInfoI(SCENET, hleDelayResult(retval, "workaround until blocking-socket", 500)); // Using hleDelayResult as a workaround for games that need blocking-socket to be implemented (ie. Coded Arms Contagion) +} + +static int sceNetInetSendto(int socket, u32 bufferPtr, int len, int flags, u32 toPtr, int tolen) { + DEBUG_LOG(SCENET, "UNTESTED %s(%i, %08x, %i, %08x, %08x, %d) at %08x", __FUNCTION__, socket, bufferPtr, len, flags, toPtr, tolen, currentMIPS->pc); + SceNetInetSockaddr* dst = (SceNetInetSockaddr*)Memory::GetCharPointer(toPtr); + int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode + flgs = convertMSGFlagsPSP2Host(flgs); + SockAddrIN4 saddr{}; + int dstlen = std::min(tolen > 0? tolen: 0, static_cast(sizeof(saddr))); + if (dst) { + saddr.addr.sa_family = dst->sa_family; + memcpy(saddr.addr.sa_data, dst->sa_data, sizeof(dst->sa_data)); + } + DEBUG_LOG(SCENET, "SendTo: Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + + std::string datahex; + DataToHexString(0, 0, Memory::GetPointer(bufferPtr), len, &datahex); + VERBOSE_LOG(SCENET, "Data Dump (%d bytes):\n%s", len, datahex.c_str()); + + int retval; + bool isBcast = isBroadcastIP(saddr.in.sin_addr.s_addr); + // Broadcast/Multicast, use real broadcast/multicast if there is no one in peerlist + if (isBcast && getActivePeerCount() > 0) { + // Acquire Peer Lock + peerlock.lock(); + SceNetAdhocctlPeerInfo* peer = friends; + for (; peer != NULL; peer = peer->next) { + // Does Skipping sending to timed out friends could cause desync when players moving group at the time MP game started? + if (peer->last_recv == 0) + continue; + + saddr.in.sin_addr.s_addr = peer->ip_addr; + retval = sendto(socket, (char*)Memory::GetPointer(bufferPtr), len, flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, dstlen); + if (retval < 0) { + DEBUG_LOG(SCENET, "SendTo(BC): Socket error %d", errno); + } + else { + DEBUG_LOG(SCENET, "SendTo(BC): Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + } + } + // Free Peer Lock + peerlock.unlock(); + retval = len; + } + // Unicast or real broadcast/multicast + else { + // FIXME: On non-Windows(including PSP too?) broadcast to INADDR_BROADCAST(255.255.255.255) might not be received by the sender itself when binded to specific IP (ie. 192.168.0.2) or INADDR_BROADCAST. + // Meanwhile, it might be received by itself when binded to subnet (ie. 192.168.0.255) or INADDR_ANY(0.0.0.0). + /*if (isBcast) { + // TODO: Replace Broadcast with Multicast to be more consistent across platform + // Replace Limited Broadcast(255.255.255.255) with Direct Broadcast(ie. 192.168.0.255) for accurate targetting when there are multiple interfaces, to avoid receiving it's own broadcasted data through IP 169.254.x.x on Windows (which is not recognized as it's own IP by the game) + // Get Local IP Address + sockaddr_in sockAddr{}; + getLocalIp(&sockAddr); + // Change the last number to 255 to indicate a common broadcast address (the accurate way should be: ip | (~subnetmask)) + ((u8*)&sockAddr.sin_addr.s_addr)[3] = 255; + saddr.in.sin_addr.s_addr = sockAddr.sin_addr.s_addr; + DEBUG_LOG(SCENET, "SendTo(BC): Address Replacement = %s", ip2str(saddr.in.sin_addr).c_str()); + }*/ + retval = sendto(socket, (char*)Memory::GetPointer(bufferPtr), len, flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, dstlen); + } + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == EAGAIN) + hleLogDebug(SCENET, retval, "errno = %d", inetLastErrno); + else + hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + return retval; + } + + return hleLogSuccessInfoI(SCENET, retval); +} + +// Similar to POSIX's sendmsg or Winsock2's WSASendMsg? Are their packets compatible one another? +// Games using this: The Warrior's Code +static int sceNetInetSendmsg(int socket, u32 msghdrPtr, int flags) { + DEBUG_LOG(SCENET, "UNTESTED %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, msghdrPtr, flags, currentMIPS->pc); + DEBUG_LOG_REPORT_ONCE(sceNetInetSendmsg, SCENET, "UNTESTED %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, msghdrPtr, flags, currentMIPS->pc); + // Note: sendmsg is concatenating iovec buffers before sending it, and send/sendto is just a wrapper for sendmsg according to https://stackoverflow.com/questions/4258834/how-sendmsg-works + int retval = -1; + if (!Memory::IsValidAddress(msghdrPtr)) { + inetLastErrno = EFAULT; + return hleLogError(SCENET, retval); + } + InetMsghdr* pspMsghdr = (InetMsghdr*)Memory::GetPointer(msghdrPtr); + int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode + flgs = convertMSGFlagsPSP2Host(flgs); + SockAddrIN4 saddr{}; +#if defined(_WIN32) + WSAMSG hdr; + WSACMSGHDR* chdr = NULL; + size_t iovecsize = sizeof(WSABUF); + WSABUF* iov = (WSABUF*)malloc(pspMsghdr->msg_iovlen * iovecsize); +#else + msghdr hdr; + cmsghdr* chdr = nullptr; + size_t iovecsize = sizeof(iovec); + iovec* iov = (iovec*)malloc(pspMsghdr->msg_iovlen * iovecsize); +#endif + if (iov == NULL) { + inetLastErrno = ENOBUFS; + return hleLogError(SCENET, retval); + } + memset(iov, 0, pspMsghdr->msg_iovlen * iovecsize); + memset(&hdr, 0, sizeof(hdr)); + if (pspMsghdr->msg_name != 0) { + SceNetInetSockaddr* pspSaddr = (SceNetInetSockaddr*)Memory::GetPointer(pspMsghdr->msg_name); + saddr.addr.sa_family = pspSaddr->sa_family; + size_t datalen = std::min(pspMsghdr->msg_namelen-(sizeof(pspSaddr->sa_len)+sizeof(pspSaddr->sa_family)), sizeof(saddr.addr.sa_data)); + memcpy(saddr.addr.sa_data, pspSaddr->sa_data, datalen); + DEBUG_LOG(SCENET, "SendMsg: Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); +#if defined(_WIN32) + hdr.name = &saddr.addr; + hdr.namelen = static_cast(datalen+sizeof(saddr.addr.sa_family)); +#else + hdr.msg_name = &saddr.addr; + hdr.msg_namelen = static_cast(datalen+sizeof(saddr.addr.sa_family)); +#endif + } +#if defined(_WIN32) + hdr.lpBuffers = iov; + hdr.dwBufferCount = pspMsghdr->msg_iovlen; +#else + hdr.msg_iov = iov; + hdr.msg_iovlen = pspMsghdr->msg_iovlen; +#endif + if (pspMsghdr->msg_iov != 0) { + SceNetIovec* pspIov = (SceNetIovec*)Memory::GetPointer(pspMsghdr->msg_iov); + for (int i = 0; i < pspMsghdr->msg_iovlen; i++) { + if (pspIov[i].iov_base != 0) { +#if defined(_WIN32) + iov[i].buf = (char*)Memory::GetPointer(pspIov[i].iov_base); + iov[i].len = pspIov[i].iov_len; +#else + iov[i].iov_base = (char*)Memory::GetPointer(pspIov[i].iov_base); + iov[i].iov_len = pspIov[i].iov_len; +#endif + } + } + } + // Control's Level (ie. host's SOL_SOCKET to/from psp's PSP_NET_INET_SOL_SOCKET) and Type (ie. SCM_RIGHTS) might need to be converted to be cross-platform + if (pspMsghdr->msg_control != 0) { +#if defined(_WIN32) + chdr = (WSACMSGHDR*)malloc(pspMsghdr->msg_controllen); +#else + chdr = (cmsghdr*)malloc(pspMsghdr->msg_controllen); +#endif + if (chdr == NULL) { + inetLastErrno = ENOBUFS; + free(iov); + return hleLogError(SCENET, retval); + } + InetCmsghdr* pspCmsghdr = (InetCmsghdr*)Memory::GetPointer(pspMsghdr->msg_control); + // TODO: Convert InetCmsghdr into platform-specific struct as they're affected by 32/64bit + memcpy(chdr, pspCmsghdr, pspMsghdr->msg_controllen); +#if defined(_WIN32) + hdr.Control.buf = (char*)chdr; // (char*)pspCmsghdr; + hdr.Control.len = pspMsghdr->msg_controllen; + // Note: Many existing implementations of CMSG_FIRSTHDR never look at msg_controllen and just return the value of cmsg_control. + if (pspMsghdr->msg_controllen >= sizeof(InetCmsghdr)) { + // TODO: Creates our own CMSG_* macros (32-bit version of it, similar to the one on PSP) to avoid alignment/size issue that can lead to memory corruption/out of bound issue. + for (WSACMSGHDR* cmsgptr = CMSG_FIRSTHDR(&hdr); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&hdr, cmsgptr)) { + cmsgptr->cmsg_type = convertCMsgTypePSP2Host(cmsgptr->cmsg_type, cmsgptr->cmsg_level); + cmsgptr->cmsg_level = convertSockoptLevelPSP2Host(cmsgptr->cmsg_level); + } + } +#else + hdr.msg_control = (char*)chdr; // (char*)pspCmsghdr; + hdr.msg_controllen = pspMsghdr->msg_controllen; + // Note: Many existing implementations of CMSG_FIRSTHDR never look at msg_controllen and just return the value of cmsg_control. + if (pspMsghdr->msg_controllen >= sizeof(InetCmsghdr)) { + for (cmsghdr* cmsgptr = CMSG_FIRSTHDR(&hdr); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&hdr, cmsgptr)) { + cmsgptr->cmsg_type = convertCMsgTypePSP2Host(cmsgptr->cmsg_type, cmsgptr->cmsg_level); + cmsgptr->cmsg_level = convertSockoptLevelPSP2Host(cmsgptr->cmsg_level); + } + } +#endif + } + // Flags (ie. PSP_NET_INET_MSG_OOB) might need to be converted to be cross-platform +#if defined(_WIN32) + hdr.dwFlags = convertMSGFlagsPSP2Host(pspMsghdr->msg_flags & ~PSP_NET_INET_MSG_DONTWAIT) | MSG_NOSIGNAL; +#else + hdr.msg_flags = convertMSGFlagsPSP2Host(pspMsghdr->msg_flags & ~PSP_NET_INET_MSG_DONTWAIT) | MSG_NOSIGNAL; +#endif + unsigned long sent = 0; + bool isBcast = isBroadcastIP(saddr.in.sin_addr.s_addr); + // Broadcast/Multicast, use real broadcast/multicast if there is no one in peerlist + if (isBcast && getActivePeerCount() > 0) { + // Acquire Peer Lock + peerlock.lock(); + SceNetAdhocctlPeerInfo* peer = friends; + for (; peer != NULL; peer = peer->next) { + // Does Skipping sending to timed out friends could cause desync when players moving group at the time MP game started? + if (peer->last_recv == 0) + continue; + + saddr.in.sin_addr.s_addr = peer->ip_addr; +#if defined(_WIN32) + int result = WSASendMsg(socket, &hdr, flgs | MSG_NOSIGNAL, &sent, NULL, NULL); + if (static_cast(sent) > retval) + retval = sent; +#else + size_t result = sendmsg(socket, &hdr, flgs | MSG_NOSIGNAL); + if (static_cast(result) > retval) + retval = result; +#endif + if (retval != SOCKET_ERROR) { + DEBUG_LOG(SCENET, "SendMsg(BC): Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); + } + else { + DEBUG_LOG(SCENET, "SendMsg(BC): Socket error %d", errno); + } + } + // Free Peer Lock + peerlock.unlock(); + // TODO: Calculate number of bytes supposed to be sent + retval = std::max(retval, 0); // Broadcast always success? + } + // Unicast or real broadcast/multicast + else { + // FIXME: On non-Windows(including PSP too?) broadcast to INADDR_BROADCAST(255.255.255.255) might not be received by the sender itself when binded to specific IP (ie. 192.168.0.2) or INADDR_BROADCAST. + // Meanwhile, it might be received by itself when binded to subnet (ie. 192.168.0.255) or INADDR_ANY(0.0.0.0). + /*if (isBcast) { + // TODO: Replace Broadcast with Multicast to be more consistent across platform + // Replace Limited Broadcast(255.255.255.255) with Direct Broadcast(ie. 192.168.0.255) for accurate targetting when there are multiple interfaces, to avoid receiving it's own broadcasted data through IP 169.254.x.x on Windows (which is not recognized as it's own IP by the game) + // Get Local IP Address + sockaddr_in sockAddr{}; + getLocalIp(&sockAddr); + // Change the last number to 255 to indicate a common broadcast address (the accurate way should be: ip | (~subnetmask)) + ((u8*)&sockAddr.sin_addr.s_addr)[3] = 255; + saddr.in.sin_addr.s_addr = sockAddr.sin_addr.s_addr; + DEBUG_LOG(SCENET, "SendMsg(BC): Address Replacement = %s", ip2str(saddr.in.sin_addr).c_str()); + }*/ +#if defined(_WIN32) + int result = WSASendMsg(socket, &hdr, flgs | MSG_NOSIGNAL, &sent, NULL, NULL); + if (result != SOCKET_ERROR) + retval = sent; +#else + retval = sendmsg(socket, &hdr, flgs | MSG_NOSIGNAL); +#endif + } + free(chdr); + free(iov); +/* // Example with 1 Msg buffer and without CMsg + msghdr msg; + iovec iov[1]; + int buflen = pspMsghdr->msg_iovlen; + char* buf = (char*)malloc(buflen); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + iov[0].iov_base = buf; + iov[0].iov_len = buflen; + + retval = sendmsg(socket, &msg, flags); + free(buf); +*/ + if (retval < 0) { + inetLastErrno = errno; + if (inetLastErrno == EAGAIN) + hleLogDebug(SCENET, retval, "errno = %d", inetLastErrno); + else + hleLogError(SCENET, retval, "errno = %d", inetLastErrno); + return retval; + } + return hleLogSuccessInfoI(SCENET, retval); // returns number of bytes sent? +} + +// Similar to POSIX's recvmsg or Mswsock's WSARecvMsg? Are their packets compatible one another? +// Games using this: World of Poker +static int sceNetInetRecvmsg(int socket, u32 msghdrPtr, int flags) { + ERROR_LOG(SCENET, "UNIMPL %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, msghdrPtr, flags, currentMIPS->pc); + DEBUG_LOG_REPORT_ONCE(sceNetInetRecvmsg, SCENET, "UNIMPL %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, msghdrPtr, flags, currentMIPS->pc); + // Reference: http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch14lev1sec5.html + int retval = -1; + if (!Memory::IsValidAddress(msghdrPtr)) { + inetLastErrno = EFAULT; + return hleLogError(SCENET, retval); + } + InetMsghdr* pspMsghdr = (InetMsghdr*)Memory::GetPointer(msghdrPtr); + int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode + flgs = convertMSGFlagsPSP2Host(flgs); + SockAddrIN4 saddr{}; +#if defined(_WIN32) + WSAMSG hdr; + WSACMSGHDR* chdr = NULL; + size_t iovecsize = sizeof(WSABUF); + WSABUF* iov = (WSABUF*)malloc(pspMsghdr->msg_iovlen * iovecsize); +#else + msghdr hdr; + cmsghdr* chdr = nullptr; + size_t iovecsize = sizeof(iovec); + iovec* iov = (iovec*)malloc(pspMsghdr->msg_iovlen * iovecsize); +#endif + if (iov == NULL) { + inetLastErrno = ENOBUFS; + return hleLogError(SCENET, retval); + } + memset(iov, 0, pspMsghdr->msg_iovlen * iovecsize); + memset(&hdr, 0, sizeof(hdr)); + // TODO: Do similar to the already working sceNetInetSendmsg but in reverse + //if (pspMsghdr->msg_name != 0) { ... } + + + return hleLogError(SCENET, retval); // returns number of bytes received? } -int sceNetApctlConnect(int connIndex) { - WARN_LOG(SCENET, "UNTESTED %s(%i)", __FUNCTION__, connIndex); - // Is this connIndex is the index to the scanning's result data or sceNetApctlGetBSSDescIDListUser result? - __UpdateApctlHandlers(0, 0, PSP_NET_APCTL_EVENT_CONNECT_REQUEST, 0); +int sceNetApctlConnect(int confId) { + WARN_LOG(SCENET, "UNTESTED %s(%i)", __FUNCTION__, confId); + if (!g_Config.bEnableWlan) + return hleLogError(SCENET, ERROR_NET_APCTL_WLAN_SWITCH_OFF, "apctl wlan off"); + + if (netApctlState != PSP_NET_APCTL_STATE_DISCONNECTED) + return hleLogError(SCENET, ERROR_NET_APCTL_NOT_DISCONNECTED, "apctl not disconnected"); + + // Is this confId is the index to the scanning's result data or sceNetApctlGetBSSDescIDListUser result? + netApctlInfoId = confId; + // Note: We're borrowing AdhocServer for Grouping purpose, so we can simulate Broadcast over the internet just like Adhoc's pro-online implementation + int ret = sceNetAdhocctlConnect("INFRA"); + + if (netApctlState == PSP_NET_APCTL_STATE_DISCONNECTED) + __UpdateApctlHandlers(0, PSP_NET_APCTL_STATE_JOINING, PSP_NET_APCTL_EVENT_CONNECT_REQUEST, 0); //hleDelayResult(0, "give time to init/cleanup", adhocEventDelayMS * 1000); - return 0; + // TODO: Blocks current thread and wait for a state change to prevent user-triggered connection attempt from causing events to piles up + return hleLogDebug(SCENET, 0, "connect = %i", ret); } -static int sceNetApctlDisconnect() { - ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); +int sceNetApctlDisconnect() { + WARN_LOG(SCENET, "UNTESTED %s()", __FUNCTION__); // Like its 'sister' function sceNetAdhocctlDisconnect, we need to alert Apctl handlers that a disconnect took place // or else games like Phantasy Star Portable 2 will hang at certain points (e.g. returning to the main menu after trying to connect to PSN). + // Note: Since we're borrowing AdhocServer for Grouping purpose, we should disconnect too + sceNetAdhocctlDisconnect(); - __UpdateApctlHandlers(0, 0, PSP_NET_APCTL_EVENT_DISCONNECT_REQUEST, 0); + // Discards any pending events so we can disconnect immediately + apctlEvents.clear(); + __UpdateApctlHandlers(netApctlState, PSP_NET_APCTL_STATE_DISCONNECTED, PSP_NET_APCTL_EVENT_DISCONNECT_REQUEST, 0); + // TODO: Blocks current thread and wait for a state change, but the state should probably need to be changed within 1 frame-time (~16ms) return 0; } @@ -1220,11 +3203,15 @@ static int sceNetApctlGetState(u32 pStateAddr) { } int NetApctl_ScanUser() { + if (!g_Config.bEnableWlan) + return hleLogError(SCENET, ERROR_NET_APCTL_WLAN_SWITCH_OFF, "apctl wlan off"); + // Scan probably only works when not in connected state, right? if (netApctlState != PSP_NET_APCTL_STATE_DISCONNECTED) return hleLogError(SCENET, ERROR_NET_APCTL_NOT_DISCONNECTED, "apctl not disconnected"); - __UpdateApctlHandlers(0, 0, PSP_NET_APCTL_EVENT_SCAN_REQUEST, 0); + __UpdateApctlHandlers(0, PSP_NET_APCTL_STATE_SCANNING, PSP_NET_APCTL_EVENT_SCAN_REQUEST, 0); + // TODO: Blocks current thread and wait for a state change to prevent user-triggered scan attempt from causing events to piles up return 0; } @@ -1379,6 +3366,141 @@ static int sceNetResolverInit() return 0; } +static int sceNetResolverTerm() +{ + ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); + netResolvers.clear(); // Let's not leaks! JPCSP doesn't clear or removes created resolvers on Term, may be because it uses SceUID ? + return 0; +} + +// Note: timeouts are in seconds +int NetResolver_StartNtoA(int rid, u32 hostnamePtr, u32 inAddrPtr, int timeout, int retry) +{ + if (netResolvers.find(rid) == netResolvers.end()) + return hleLogError(SCENET, ERROR_NET_RESOLVER_BAD_ID, "Bad Resolver Id: %i", rid); + + auto& resolver = netResolvers[rid]; + addrinfo* resolved = nullptr; + std::string err, hostname = std::string(safe_string(Memory::GetCharPointer(hostnamePtr))); + SockAddrIN4 addr{}; + addr.in.sin_addr.s_addr = INADDR_NONE; + // TODO: Use a lightweight DNS Resolver library (https://github.com/wahern/dns or https://github.com/CesiumComputer/sldr or https://github.com/b17v134/dns may be), + // So we can support Custom DNS Server without users messing around with Network adapter settings. + // Also need to implement built-in hosts file to avoid users from changing system hosts file which requires admin/sudo (for users with enforced DNS server by their ISP, thus unable to use custom DNS server). + // TODO: Resolves using Primary DNS (& Secondary DNS) Server set on Apctl. + // FIXME: Should probably do this on a background thread, at least for the Async version + resolver.isRunning = true; + if (!net::DNSResolve(hostname, "", &resolved, err)) { + // TODO: Return an error based on the outputted "err" (unfortunately it's already converted to string) + return hleLogError(SCENET, ERROR_NET_RESOLVER_INVALID_HOST, "DNS Error Resolving %s (%s)\n", hostname.c_str(), err.c_str()); + } + if (resolved) { + for (auto ptr = resolved; ptr != NULL; ptr = ptr->ai_next) { + switch (ptr->ai_family) { + case AF_INET: + addr.in = *(sockaddr_in*)ptr->ai_addr; + break; + } + } + net::DNSResolveFree(resolved); + + Memory::Write_U32(addr.in.sin_addr.s_addr, inAddrPtr); + INFO_LOG(SCENET, "%s - Hostname: %s => IPv4: %s", __FUNCTION__, hostname.c_str(), ip2str(addr.in.sin_addr, false).c_str()); + } + resolver.isRunning = false; + + return 0; +} + +static int sceNetResolverStartNtoA(int rid, u32 hostnamePtr, u32 inAddrPtr, int timeout, int retry) +{ + WARN_LOG(SCENET, "UNTESTED %s(%d, %08x, %08x, %d, %d) at %08x", __FUNCTION__, rid, hostnamePtr, inAddrPtr, timeout, retry, currentMIPS->pc); + return NetResolver_StartNtoA(rid, hostnamePtr, inAddrPtr, timeout, retry); +} + +static int sceNetResolverStartNtoAAsync(int rid, u32 hostnamePtr, u32 inAddrPtr, int timeout, int retry) +{ + ERROR_LOG_REPORT_ONCE(sceNetResolverStartNtoAAsync, SCENET, "UNIMPL %s(%d, %08x, %08x, %d, %d) at %08x", __FUNCTION__, rid, hostnamePtr, inAddrPtr, timeout, retry, currentMIPS->pc); + return NetResolver_StartNtoA(rid, hostnamePtr, inAddrPtr, timeout, retry); +} + +// FIXME: May be used to get the resolver.isRunning of one or more resolver(s)? +static int sceNetResolverPollAsync(int rid, u32 unknown) +{ + ERROR_LOG_REPORT_ONCE(sceNetResolverPollAsync, SCENET, "UNIMPL %s(%d, %08x) at %08x", __FUNCTION__, rid, unknown, currentMIPS->pc); + return 0; +} + +// FIXME: May be used to block current thread until resolver.isRunning = false? +static int sceNetResolverWaitAsync(int rid, u32 unknown) +{ + ERROR_LOG_REPORT_ONCE(sceNetResolverWaitAsync, SCENET, "UNIMPL %s(%d, %08x) at %08x", __FUNCTION__, rid, unknown, currentMIPS->pc); + return 0; +} + +// FIXME: Something like [the deprecated] gethostbyname may be? +static int sceNetResolverStartAtoN(int rid, u32 inAddr, u32 hostnamePtr, int hostnameLength, int timeout, int retry) +{ + ERROR_LOG_REPORT_ONCE(sceNetResolverStartAtoN, SCENET, "UNIMPL %s(%d, %08x[%s], %08x, %i, %i, %i) at %08x", __FUNCTION__, rid, inAddr, ip2str(*(in_addr*)&inAddr, false).c_str(), hostnamePtr, hostnameLength, timeout, retry, currentMIPS->pc); + return 0; +} + +static int sceNetResolverStartAtoNAsync(int rid, u32 inAddr, u32 hostnamePtr, int hostnameLength, int timeout, int retry) +{ + ERROR_LOG_REPORT_ONCE(sceNetResolverStartAtoNAsync, SCENET, "UNIMPL %s(%d, %08x[%s], %08x, %i, %i, %i) at %08x", __FUNCTION__, rid, inAddr, ip2str(*(in_addr*)&inAddr, false).c_str(), hostnamePtr, hostnameLength, timeout, retry, currentMIPS->pc); + return 0; +} + +// FIXME: What to do with the [temporary] buffer Args? as input or output? is it mandatory or optional? +static int sceNetResolverCreate(u32 ridPtr, u32 bufferPtr, int bufferLen) +{ + WARN_LOG(SCENET, "UNTESTED %s(%08x[%d], %08x, %d) at %08x", __FUNCTION__, ridPtr, Memory::Read_U32(ridPtr), bufferPtr, bufferLen, currentMIPS->pc); + if (!Memory::IsValidRange(ridPtr, 4)) + return hleLogError(SCENET, ERROR_NET_RESOLVER_INVALID_PTR, "Invalid Ptr: %08x", ridPtr); + + if (Memory::IsValidRange(bufferPtr, 4) && bufferLen < 1) + return hleLogError(SCENET, ERROR_NET_RESOLVER_INVALID_BUFLEN, "Invalid Buffer Length: %i", bufferLen); + + // Note: JPCSP uses SceUidManager to generate the id + NetResolver resolver = { 0 }; + int rid = 1; // FIXME: Is this id starts from 1 (as most id) ? There should be MAX_RESOLVERS_ID too (may be 32 ?) as there is ERROR_NET_RESOLVER_ID_MAX error + while (netResolvers.find(rid) != netResolvers.end()) + ++rid; + + // TODO: Need to confirm the isRunning status right after creation, JPCSP seems to set the value to true on Create instead of on Start + resolver.id = rid; + resolver.bufferAddr = bufferPtr; + resolver.bufferLen = bufferLen; + netResolvers[rid] = resolver; + + Memory::Write_U32(rid, ridPtr); + return 0; +} + +static int sceNetResolverStop(int rid) +{ + WARN_LOG(SCENET, "UNTESTED %s(%d) at %08x", __FUNCTION__, rid, currentMIPS->pc); + if (netResolvers.find(rid) == netResolvers.end()) + return hleLogError(SCENET, ERROR_NET_RESOLVER_BAD_ID, "Bad Resolver Id: %i", rid); + + auto& resolver = netResolvers[rid]; + if (!resolver.isRunning) + return hleLogError(SCENET, ERROR_NET_RESOLVER_ALREADY_STOPPED, "Resolver Already Stopped (Id: %i)", rid); + + resolver.isRunning = false; + return 0; +} + +static int sceNetResolverDelete(int rid) +{ + WARN_LOG(SCENET, "UNTESTED %s(%d) at %08x", __FUNCTION__, rid, currentMIPS->pc); + if (netResolvers.find(rid) == netResolvers.end()) + return hleLogError(SCENET, ERROR_NET_RESOLVER_BAD_ID, "Bad Resolver Id: %i", rid); + + netResolvers.erase(rid); + return 0; +} + static int sceNetApctlAddInternalHandler(u32 handlerPtr, u32 handlerArg) { ERROR_LOG(SCENET, "UNIMPL %s(%08x, %08x)", __FUNCTION__, handlerPtr, handlerArg); // This seems to be a 2nd kind of handler @@ -1432,34 +3554,61 @@ static int sceNetApctl_lib2_C20A144C(int connIndex, u32 ps3MacAddressPtr) { return sceNetApctlConnect(connIndex); } - +// unknown1 = source port for sending SSDP packet? unknown2 = discovery timeout? static int sceNetUpnpInit(int unknown1,int unknown2) { - ERROR_LOG_REPORT_ONCE(sceNetUpnpInit, SCENET, "UNIMPLsceNetUpnpInit %d,%d",unknown1,unknown2); + ERROR_LOG_REPORT_ONCE(sceNetUpnpInit, SCENET, "UNIMPL sceNetUpnpInit(%d, %d)", unknown1, unknown2); return 0; } static int sceNetUpnpStart() { - ERROR_LOG(SCENET, "UNIMPLsceNetUpnpStart"); + ERROR_LOG(SCENET, "UNIMPL sceNetUpnpStart()"); return 0; } static int sceNetUpnpStop() { - ERROR_LOG(SCENET, "UNIMPLsceNetUpnpStop"); + ERROR_LOG(SCENET, "UNIMPL sceNetUpnpStop()"); return 0; } static int sceNetUpnpTerm() { - ERROR_LOG(SCENET, "UNIMPLsceNetUpnpTerm"); + ERROR_LOG(SCENET, "UNIMPL sceNetUpnpTerm()"); + return 0; +} + +static int sceNetUpnpGetNatInfo(u32 unkStructPtr) +{ + ERROR_LOG(SCENET, "UNIMPL sceNetUpnpGetNatInfo(%08x)", unkStructPtr); + + // Unknown struct of 16 bytes + Memory::Memset(unkStructPtr, 0, 16); + + return 0; +} + +static int sceNetUpnp_8513C6D1(u32 unk1, u32 unk2, u32 unk3) +{ + ERROR_LOG(SCENET, "UNIMPL sceNetUpnp_8513C6D1(%08x, %08x, %08x)", unk1, unk2, unk3); return 0; } -static int sceNetUpnpGetNatInfo() +static int sceNetUpnp_FDA78483() { - ERROR_LOG(SCENET, "UNIMPLsceNetUpnpGetNatInfo"); + ERROR_LOG(SCENET, "UNIMPL sceNetUpnp_FDA78483()"); + return 0; +} + +static int sceNetUpnp_1038E77A(u32 unkStructPtr) +{ + ERROR_LOG(SCENET, "UNIMPL sceNetUpnp_1038E77A(%08x)", unkStructPtr); + + // Unknown struct of 48 bytes + Memory::Memset(unkStructPtr, 0, 48); + Memory::Write_U32(1, unkStructPtr + 4); + return 0; } @@ -1489,63 +3638,63 @@ const HLEFunction sceNet[] = { }; const HLEFunction sceNetResolver[] = { - {0X224C5F44, nullptr, "sceNetResolverStartNtoA", '?', "" }, - {0X244172AF, nullptr, "sceNetResolverCreate", '?', "" }, - {0X94523E09, nullptr, "sceNetResolverDelete", '?', "" }, - {0XF3370E61, &WrapI_V, "sceNetResolverInit", 'i', "" }, - {0X808F6063, nullptr, "sceNetResolverStop", '?', "" }, - {0X6138194A, nullptr, "sceNetResolverTerm", '?', "" }, - {0X629E2FB7, nullptr, "sceNetResolverStartAtoN", '?', "" }, - {0X14C17EF9, nullptr, "sceNetResolverStartNtoAAsync", '?', "" }, - {0XAAC09184, nullptr, "sceNetResolverStartAtoNAsync", '?', "" }, - {0X12748EB9, nullptr, "sceNetResolverWaitAsync", '?', "" }, - {0X4EE99358, nullptr, "sceNetResolverPollAsync", '?', "" }, -}; + {0X224C5F44, &WrapI_IUUII, "sceNetResolverStartNtoA", 'i', "ixxii" }, + {0X244172AF, &WrapI_UUI, "sceNetResolverCreate", 'i', "xxi" }, + {0X94523E09, &WrapI_I, "sceNetResolverDelete", 'i', "i" }, + {0XF3370E61, &WrapI_V, "sceNetResolverInit", 'i', "" }, + {0X808F6063, &WrapI_I, "sceNetResolverStop", 'i', "i" }, + {0X6138194A, &WrapI_V, "sceNetResolverTerm", 'i', "" }, + {0X629E2FB7, &WrapI_IUUIII, "sceNetResolverStartAtoN", 'i', "ixxiii"}, + {0X14C17EF9, &WrapI_IUUII, "sceNetResolverStartNtoAAsync", 'i', "ixxii" }, + {0XAAC09184, &WrapI_IUUIII,"sceNetResolverStartAtoNAsync", 'i', "ixxiii"}, + {0X12748EB9, &WrapI_IU, "sceNetResolverWaitAsync", 'i', "ix" }, + {0X4EE99358, &WrapI_IU, "sceNetResolverPollAsync", 'i', "ix" }, +}; const HLEFunction sceNetInet[] = { - {0X17943399, &WrapI_V, "sceNetInetInit", 'i', "" }, - {0X4CFE4E56, nullptr, "sceNetInetShutdown", '?', "" }, - {0XA9ED66B9, &WrapI_V, "sceNetInetTerm", 'i', "" }, - {0X8B7B220F, &WrapI_III, "sceNetInetSocket", 'i', "iii" }, - {0X2FE71FE7, &WrapI_IIIUI, "sceNetInetSetsockopt", 'i', "iiixi"}, - {0X4A114C7C, nullptr, "sceNetInetGetsockopt", '?', "" }, - {0X410B34AA, &WrapI_IUI, "sceNetInetConnect", 'i', "ixi" }, - {0X805502DD, nullptr, "sceNetInetCloseWithRST", '?', "" }, - {0XD10A1A7A, nullptr, "sceNetInetListen", '?', "" }, - {0XDB094E1B, nullptr, "sceNetInetAccept", '?', "" }, - {0XFAABB1DD, &WrapI_VUI, "sceNetInetPoll", 'i', "pxi" }, - {0X5BE8D595, nullptr, "sceNetInetSelect", '?', "" }, - {0X8D7284EA, nullptr, "sceNetInetClose", '?', "" }, - {0XCDA85C99, &WrapI_IUUU, "sceNetInetRecv", 'i', "ixxx" }, - {0XC91142E4, nullptr, "sceNetInetRecvfrom", '?', "" }, - {0XEECE61D2, nullptr, "sceNetInetRecvmsg", '?', "" }, - {0X7AA671BC, &WrapI_IUUU, "sceNetInetSend", 'i', "ixxx" }, - {0X05038FC7, nullptr, "sceNetInetSendto", '?', "" }, - {0X774E36F4, nullptr, "sceNetInetSendmsg", '?', "" }, - {0XFBABE411, &WrapI_V, "sceNetInetGetErrno", 'i', "" }, - {0X1A33F9AE, nullptr, "sceNetInetBind", '?', "" }, - {0XB75D5B0A, nullptr, "sceNetInetInetAddr", '?', "" }, - {0X1BDF5D13, &WrapI_CU, "sceNetInetInetAton", 'i', "sx" }, - {0XD0792666, nullptr, "sceNetInetInetNtop", '?', "" }, - {0XE30B8C19, nullptr, "sceNetInetInetPton", '?', "" }, - {0X8CA3A97E, nullptr, "sceNetInetGetPspError", '?', "" }, - {0XE247B6D6, nullptr, "sceNetInetGetpeername", '?', "" }, - {0X162E6FD5, nullptr, "sceNetInetGetsockname", '?', "" }, - {0X80A21ABD, nullptr, "sceNetInetSocketAbort", '?', "" }, - {0X39B0C7D3, nullptr, "sceNetInetGetUdpcbstat", '?', "" }, - {0XB3888AD4, nullptr, "sceNetInetGetTcpcbstat", '?', "" }, + {0X17943399, &WrapI_V, "sceNetInetInit", 'i', "" }, + {0X4CFE4E56, &WrapI_II, "sceNetInetShutdown", 'i', "ii" }, + {0XA9ED66B9, &WrapI_V, "sceNetInetTerm", 'i', "" }, + {0X8B7B220F, &WrapI_III, "sceNetInetSocket", 'i', "iii" }, + {0X2FE71FE7, &WrapI_IIIUI, "sceNetInetSetsockopt", 'i', "iiixi" }, + {0X4A114C7C, &WrapI_IIIUU, "sceNetInetGetsockopt", 'i', "iiixx" }, + {0X410B34AA, &WrapI_IUI, "sceNetInetConnect", 'i', "ixi" }, + {0X805502DD, &WrapI_I, "sceNetInetCloseWithRST", 'i', "i" }, + {0XD10A1A7A, &WrapI_II, "sceNetInetListen", 'i', "ii" }, + {0XDB094E1B, &WrapI_IUU, "sceNetInetAccept", 'i', "ixx" }, + {0XFAABB1DD, &WrapI_UUI, "sceNetInetPoll", 'i', "xxi" }, + {0X5BE8D595, &WrapI_IUUUU, "sceNetInetSelect", 'i', "ixxxx" }, + {0X8D7284EA, &WrapI_I, "sceNetInetClose", 'i', "i" }, + {0XCDA85C99, &WrapI_IUUU, "sceNetInetRecv", 'i', "ixxx" }, + {0XC91142E4, &WrapI_IUIIUU, "sceNetInetRecvfrom", 'i', "ixiixx" }, + {0XEECE61D2, &WrapI_IUI, "sceNetInetRecvmsg", 'i', "ixi" }, + {0X7AA671BC, &WrapI_IUUU, "sceNetInetSend", 'i', "ixxx" }, + {0X05038FC7, &WrapI_IUIIUI, "sceNetInetSendto", 'i', "ixiixi" }, + {0X774E36F4, &WrapI_IUI, "sceNetInetSendmsg", 'i', "ixi" }, + {0XFBABE411, &WrapI_V, "sceNetInetGetErrno", 'i', "" }, + {0X1A33F9AE, &WrapI_IUI, "sceNetInetBind", 'i', "ixi" }, + {0XB75D5B0A, &WrapU_C, "sceNetInetInetAddr", 'x', "s" }, + {0X1BDF5D13, &WrapI_CU, "sceNetInetInetAton", 'i', "sx" }, + {0XD0792666, &WrapU_IUUU, "sceNetInetInetNtop", 'x', "ixxx" }, + {0XE30B8C19, &WrapI_ICU, "sceNetInetInetPton", 'i', "isx" }, + {0X8CA3A97E, &WrapI_V, "sceNetInetGetPspError", 'i', "" }, + {0XE247B6D6, &WrapI_IUU, "sceNetInetGetpeername", 'i', "ixx" }, + {0X162E6FD5, &WrapI_IUU, "sceNetInetGetsockname", 'i', "ixx" }, + {0X80A21ABD, &WrapI_I, "sceNetInetSocketAbort", 'i', "i" }, + {0X39B0C7D3, nullptr, "sceNetInetGetUdpcbstat", '?', "" }, + {0XB3888AD4, nullptr, "sceNetInetGetTcpcbstat", '?', "" }, }; const HLEFunction sceNetApctl[] = { - {0XCFB957C6, &WrapI_I, "sceNetApctlConnect", 'i', "i" }, - {0X24FE91A1, &WrapI_V, "sceNetApctlDisconnect", 'i', "" }, - {0X5DEAC81B, &WrapI_U, "sceNetApctlGetState", 'i', "x" }, - {0X8ABADD51, &WrapU_UU, "sceNetApctlAddHandler", 'x', "xx" }, - {0XE2F91F9B, &WrapI_II, "sceNetApctlInit", 'i', "ii" }, - {0X5963991B, &WrapI_U, "sceNetApctlDelHandler", 'i', "x" }, - {0XB3EDD0EC, &WrapI_V, "sceNetApctlTerm", 'i', "" }, - {0X2BEFDF23, &WrapI_IU, "sceNetApctlGetInfo", 'i', "ix" }, - {0XA3E77E13, &WrapI_V, "sceNetApctlScanSSID2", 'i', "" }, + {0XCFB957C6, &WrapI_I, "sceNetApctlConnect", 'i', "i" }, + {0X24FE91A1, &WrapI_V, "sceNetApctlDisconnect", 'i', "" }, + {0X5DEAC81B, &WrapI_U, "sceNetApctlGetState", 'i', "x" }, + {0X8ABADD51, &WrapU_UU, "sceNetApctlAddHandler", 'x', "xx" }, + {0XE2F91F9B, &WrapI_II, "sceNetApctlInit", 'i', "ii" }, + {0X5963991B, &WrapI_U, "sceNetApctlDelHandler", 'i', "x" }, + {0XB3EDD0EC, &WrapI_V, "sceNetApctlTerm", 'i', "" }, + {0X2BEFDF23, &WrapI_IU, "sceNetApctlGetInfo", 'i', "ix" }, + {0XA3E77E13, &WrapI_V, "sceNetApctlScanSSID2", 'i', "" }, {0XE9B2E5E6, &WrapI_V, "sceNetApctlScanUser", 'i', "" }, {0XF25A5006, &WrapI_UUUU, "sceNetApctlGetBSSDescIDList2", 'i', "xxxx" }, {0X2935C45B, &WrapI_IIU, "sceNetApctlGetBSSDescEntry2", 'i', "iix" }, @@ -1573,11 +3722,14 @@ const HLEFunction sceWlanDrv[] = { // see http://www.kingx.de/forum/showthread.php?tid=35164 const HLEFunction sceNetUpnp[] = { - {0X27045362, &WrapI_V, "sceNetUpnpGetNatInfo", 'i', "" }, + {0X27045362, &WrapI_U, "sceNetUpnpGetNatInfo", 'i', "x" }, {0X3432B2E5, &WrapI_V, "sceNetUpnpStart", 'i', "" }, {0X3E32ED9E, &WrapI_V, "sceNetUpnpStop", 'i', "" }, {0X540491EF, &WrapI_V, "sceNetUpnpTerm", 'i', "" }, {0XE24220B5, &WrapI_II, "sceNetUpnpInit", 'i', "ii" }, + {0X8513C6D1, &WrapI_UUU, "sceNetUpnp_8513C6D1", 'i', "xxx" }, + {0XFDA78483, &WrapI_V, "sceNetUpnp_FDA78483", 'i', "" }, + {0X1038E77A, &WrapI_U, "sceNetUpnp_1038E77A", 'i', "x" }, }; const HLEFunction sceNetIfhandle[] = { @@ -1592,6 +3744,11 @@ void Register_sceNet() { RegisterModule("sceNetApctl", ARRAY_SIZE(sceNetApctl), sceNetApctl); } +// Alias of sceNetApctl, used by SCEJ PSP Browser (UTST-99261) +void Register_sceNetApctl_internal_user() { + RegisterModule("sceNetApctl_internal_user", ARRAY_SIZE(sceNetApctl), sceNetApctl); +} + void Register_sceWlanDrv() { RegisterModule("sceWlanDrv", ARRAY_SIZE(sceWlanDrv), sceWlanDrv); } diff --git a/Core/HLE/sceNet.h b/Core/HLE/sceNet.h index d34ab560dd2c..b1904eb5756b 100644 --- a/Core/HLE/sceNet.h +++ b/Core/HLE/sceNet.h @@ -28,22 +28,244 @@ #define PSP_THREAD_ATTR_USER 0x80000000 #endif -// Option Names -#define PSP_SO_REUSEPORT 0x0200 -#define PSP_SO_NBIO 0x1009 -// Infrastructure Errno Numbers -#define INET_EAGAIN 0x0B -#define INET_ETIMEDOUT 0x74 -#define INET_EINPROGRESS 0x77 -#define INET_EISCONN 0x7F - -// On-Demand Nonblocking Flag -#define INET_MSG_DONTWAIT 0x80 +// Socket Types (based on https://github.com/justincormack/netbsd-src/blob/master/src/sys/sys/socket.h ) +#define PSP_NET_INET_SOCK_STREAM 1 // stream socket +#define PSP_NET_INET_SOCK_DGRAM 2 // datagram socket +#define PSP_NET_INET_SOCK_RAW 3 // raw-protocol interface // SOCK_RAW is similar to but not compatible with the obsolete AF_INET / SOCK_PACKET // SOCK_RAW have some restrictions on newer Windows https://docs.microsoft.com/en-us/windows/win32/winsock/tcp-ip-raw-sockets-2 +#define PSP_NET_INET_SOCK_RDM 4 // reliably-delivered message +#define PSP_NET_INET_SOCK_SEQPACKET 5 // sequenced packet stream +#define PSP_NET_INET_SOCK_CONN_DGRAM 6 // connection-orientated datagram +#define PSP_NET_INET_SOCK_DCCP PSP_NET_INET_SOCK_CONN_DGRAM // Datagram Congestion Control Protocol +#define PSP_NET_INET_SOCK_PACKET 10 // Linux specific way of getting packets at the dev level. For writing rarp and other similar things on the user level // SOCK_PACKET is an obsolete socket type to receive raw packets directly from the device driver +#define PSP_NET_INET_SOCK_TYPE_MASK 0x000F // mask that covers the above +// Flags to be ORed into the type parameter of socket and socketpair and used for the flags parameter of paccept. +#define PSP_NET_INET_SOCK_CLOEXEC 0x10000000 // set close on exec on socket +#define PSP_NET_INET_SOCK_NONBLOCK 0x20000000 // set non blocking i/o socket +#define PSP_NET_INET_SOCK_NOSIGPIPE 0x40000000 // don't send sigpipe +#define PSP_NET_INET_SOCK_FLAGS_MASK 0xf0000000 // flags mask + +// Option flags per-socket (based on SOL_SOCKET value on PSP (0xffff) seems to be different with linux/android's auto-generated socket.h (1), but similar to posix/gnu/BSD like this https://github.com/eblot/newlib/blob/master/newlib/libc/sys/linux/sys/socket.h ?) +#define PSP_NET_INET_SO_DEBUG 0x0001 // turn on debugging info recording +#define PSP_NET_INET_SO_ACCEPTCONN 0x0002 // socket has had listen() +#define PSP_NET_INET_SO_REUSEADDR 0x0004 // allow local address reuse +#define PSP_NET_INET_SO_KEEPALIVE 0x0008 // keep connections alive +#define PSP_NET_INET_SO_DONTROUTE 0x0010 // just use interface addresses +#define PSP_NET_INET_SO_BROADCAST 0x0020 // permit sending of broadcast msgs +#define PSP_NET_INET_SO_USELOOPBACK 0x0040 // bypass hardware when possible +#define PSP_NET_INET_SO_LINGER 0x0080 // linger on close if data present +#define PSP_NET_INET_SO_OOBINLINE 0x0100 // leave received OOB data in line +#define PSP_NET_INET_SO_REUSEPORT 0x0200 // allow local address & port reuse +#define PSP_NET_INET_SO_TIMESTAMP 0x0400 // timestamp received dgram traffic +#define PSP_NET_INET_SO_ONESBCAST 0x0800 // permit sending to 255.255.255.255 + +// Additional options (not kept in so_options) +#define PSP_NET_INET_SO_SNDBUF 0x1001 // send buffer size (default value = 16384 bytes) +#define PSP_NET_INET_SO_RCVBUF 0x1002 // receive buffer size (default value = 16384 bytes for TCP/IP, 41600 bytes for UDP/IP) +#define PSP_NET_INET_SO_SNDLOWAT 0x1003 // send low-water mark +#define PSP_NET_INET_SO_RCVLOWAT 0x1004 // receive low-water mark +#define PSP_NET_INET_SO_SNDTIMEO 0x1005 // send timeout +#define PSP_NET_INET_SO_RCVTIMEO 0x1006 // receive timeout +#define PSP_NET_INET_SO_ERROR 0x1007 // get error status and clear +#define PSP_NET_INET_SO_TYPE 0x1008 // get socket type +#define PSP_NET_INET_SO_NBIO 0x1009 // SO_NONBLOCK ? // set to non-blocking I/O mode (on true, returning 0x80 when retrieved using getsockopt?) +#define PSP_NET_INET_SO_BIO 0x100a // set to blocking I/O mode (not using the optval just like SO_NBIO?) +//#define PSP_NET_INET_SO_NONBLOCK 0x100b // set to blocking or non-blocking I/O mode (using the optval) + +// User-settable options (used with setsockopt) +#define PSP_NET_INET_TCP_NODELAY 0x01 // don't delay send to coalesce packets +#define PSP_NET_INET_TCP_MAXSEG 0x02 // set maximum segment size + +// Options for use with [get/set]sockopt at the IP level +#define PSP_NET_INET_IP_OPTIONS 1 // (buf/ip_opts) set/get IP options +#define PSP_NET_INET_IP_HDRINCL 2 // (int) header is included with data +#define PSP_NET_INET_IP_TOS 3 // (int) IP type of service and preced. +#define PSP_NET_INET_IP_TTL 4 // (int) IP time to live +#define PSP_NET_INET_IP_RECVOPTS 5 // (bool) receive all IP opts w/dgram +#define PSP_NET_INET_IP_RECVRETOPTS 6 // (bool) receive IP opts for response +#define PSP_NET_INET_IP_RECVDSTADDR 7 // (bool) receive IP dst addr w/dgram +#define PSP_NET_INET_IP_RETOPTS 8 // (ip_opts) set/get IP options +#define PSP_NET_INET_IP_MULTICAST_IF 9 // (in_addr) set/get IP multicast i/f +#define PSP_NET_INET_IP_MULTICAST_TTL 10 // (u_char) set/get IP multicast ttl +#define PSP_NET_INET_IP_MULTICAST_LOOP 11 // (u_char) set/get IP multicast loopback +#define PSP_NET_INET_IP_ADD_MEMBERSHIP 12 // (ip_mreq) add an IP group membership +#define PSP_NET_INET_IP_DROP_MEMBERSHIP 13 // (ip_mreq) drop an IP group membership +#define PSP_NET_INET_IP_PORTRANGE 19 // (int) range to use for ephemeral port +#define PSP_NET_INET_IP_RECVIF 20 // (bool) receive reception if w/dgram +#define PSP_NET_INET_IP_ERRORMTU 21 // (int) get MTU of last xmit = EMSGSIZE + +#define PSP_NET_INET_IP_IPSEC_POLICY 22 // (struct) get/set security policy + +// Level number for [get/set]sockopt to apply to socket itself +#define PSP_NET_INET_SOL_SOCKET 0xffff // options for socket level + +// "Socket"-level control message types: +#define PSP_NET_INET_SCM_RIGHTS 0x01 // access rights (array of int) +#define PSP_NET_INET_SCM_CREDS 0x04 // credentials (struct sockcred) +#define PSP_NET_INET_SCM_TIMESTAMP 0x08 // timestamp (struct timeval) + +// Protocols +#define PSP_NET_INET_IPPROTO_IP 0 // dummy for IP +#define PSP_NET_INET_IPPROTO_HOPOPTS 0 // IP6 hop-by-hop options +#define PSP_NET_INET_IPPROTO_UNSPEC 0 // 0 will defaulted to the only existing protocol for that particular domain/family and type +#define PSP_NET_INET_IPPROTO_ICMP 1 // control message protocol +#define PSP_NET_INET_IPPROTO_IGMP 2 // group mgmt protocol +#define PSP_NET_INET_IPPROTO_GGP 3 // gateway^2 (deprecated) +#define PSP_NET_INET_IPPROTO_IPV4 4 // IP header +#define PSP_NET_INET_IPPROTO_IPIP 4 // IP inside IP +#define PSP_NET_INET_IPPROTO_TCP 6 // tcp +#define PSP_NET_INET_IPPROTO_EGP 8 // exterior gateway protocol +#define PSP_NET_INET_IPPROTO_PUP 12 // pup +#define PSP_NET_INET_IPPROTO_UDP 17 // user datagram protocol +#define PSP_NET_INET_IPPROTO_IDP 22 // xns idp +#define PSP_NET_INET_IPPROTO_TP 29 // tp-4 w/ class negotiation +#define PSP_NET_INET_IPPROTO_IPV6 41 // IP6 header +#define PSP_NET_INET_IPPROTO_ROUTING 43 // IP6 routing header +#define PSP_NET_INET_IPPROTO_FRAGMENT 44 // IP6 fragmentation header +#define PSP_NET_INET_IPPROTO_RSVP 46 // resource reservation +#define PSP_NET_INET_IPPROTO_GRE 47 // GRE encaps RFC 1701 +#define PSP_NET_INET_IPPROTO_ESP 50 // encap. security payload +#define PSP_NET_INET_IPPROTO_AH 51 // authentication header +#define PSP_NET_INET_IPPROTO_MOBILE 55 // IP Mobility RFC 2004 +#define PSP_NET_INET_IPPROTO_IPV6_ICMP 58 // IPv6 ICMP +#define PSP_NET_INET_IPPROTO_ICMPV6 58 // ICMP6 +#define PSP_NET_INET_IPPROTO_NONE 59 // IP6 no next header +#define PSP_NET_INET_IPPROTO_DSTOPTS 60 // IP6 destination option +#define PSP_NET_INET_IPPROTO_EON 80 // ISO cnlp +#define PSP_NET_INET_IPPROTO_ENCAP 98 // encapsulation header +#define PSP_NET_INET_IPPROTO_PIM 103 // Protocol indep. multicast +#define PSP_NET_INET_IPPROTO_IPCOMP 108 // IP Payload Comp. Protocol + +#define PSP_NET_INET_IPPROTO_RAW 255 // raw IP packet +#define PSP_NET_INET_IPPROTO_MAX 256 + +#define PSP_NET_INET_IPPROTO_DONE 257 // all job for this packet are done + +// Address families +#define PSP_NET_INET_AF_UNSPEC 0 // unspecified +#define PSP_NET_INET_AF_LOCAL 1 // local to host (pipes, portals) +#define PSP_NET_INET_AF_UNIX PSP_NET_INET_AF_LOCAL // backward compatibility +#define PSP_NET_INET_AF_INET 2 // internetwork: UDP, TCP, etc. +#define PSP_NET_INET_AF_IMPLINK 3 // arpanet imp addresses +#define PSP_NET_INET_AF_PUP 4 // pup protocols: e.g. BSP +#define PSP_NET_INET_AF_CHAOS 5 // mit CHAOS protocols +#define PSP_NET_INET_AF_NS 6 // XEROX NS protocols +#define PSP_NET_INET_AF_ISO 7 // ISO protocols +#define PSP_NET_INET_AF_OSI PSP_NET_INET_AF_ISO +#define PSP_NET_INET_AF_ECMA 8 // european computer manufacturers +#define PSP_NET_INET_AF_DATAKIT 9 // datakit protocols +#define PSP_NET_INET_AF_CCITT 10 // CCITT protocols, X.25 etc +#define PSP_NET_INET_AF_SNA 11 // IBM SNA +#define PSP_NET_INET_AF_DECnet 12 // DECnet +#define PSP_NET_INET_AF_DLI 13 // DEC Direct data link interface +#define PSP_NET_INET_AF_LAT 14 // LAT +#define PSP_NET_INET_AF_HYLINK 15 // NSC Hyperchannel +#define PSP_NET_INET_AF_APPLETALK 16 // Apple Talk +#define PSP_NET_INET_AF_ROUTE 17 // Internal Routing Protocol +#define PSP_NET_INET_AF_LINK 18 // Link layer interface + +#define PSP_NET_INET_AF_COIP 20 // connection-oriented IP, aka ST II +#define PSP_NET_INET_AF_CNT 21 // Computer Network Technology + +#define PSP_NET_INET_AF_IPX 23 // Novell Internet Protocol +#define PSP_NET_INET_AF_INET6 24 // IP version 6 + +#define PSP_NET_INET_AF_ISDN 26 // Integrated Services Digital Network +#define PSP_NET_INET_AF_E164 PSP_NET_INET_AF_ISDN // CCITT E.164 recommendation +#define PSP_NET_INET_AF_NATM 27 // native ATM access +#define PSP_NET_INET_AF_ARP 28 // (rev.) addr. res. prot. (RFC 826) + +#define PSP_NET_INET_AF_MAX 31 + +// Infrastructure ERRNO Values (similar to this https://github.com/eblot/newlib/blob/master/newlib/libc/include/sys/errno.h ?) +#define ERROR_INET_EINTR 4 // Interrupted system call +#define ERROR_INET_EBADF 9 //0x09 // Or was it 0x80010009 (SCE_ERROR_ERRNO_EBADF/SCE_KERNEL_ERROR_ERRNO_INVALID_FILE_DESCRIPTOR) ? +#define ERROR_INET_EAGAIN 11 //0x0B // Or was it 0x8001000B (SCE_ERROR_ERRNO_EAGAIN) ? +#define ERROR_INET_EWOULDBLOCK EAGAIN // Operation would block +#define ERROR_INET_EACCES 13 // Permission denied +#define ERROR_INET_EFAULT 14 // Bad address +#define ERROR_INET_EINVAL 22 // Invalid argument +#define ERROR_INET_ENOSPC 28 // No space left on device +#define ERROR_INET_EPIPE 32 // Broken pipe +#define ERROR_INET_ENOMSG 35 // No message of desired type +#define ERROR_INET_ENOLINK 67 // The link has been severed +#define ERROR_INET_EPROTO 71 // Protocol error +#define ERROR_INET_EBADMSG 77 // Trying to read unreadable message +#define ERROR_INET_EOPNOTSUPP 95 // Operation not supported on transport endpoint +#define ERROR_INET_EPFNOSUPPORT 96 // Protocol family not supported +#define ERROR_INET_ECONNRESET 104 // Connection reset by peer +#define ERROR_INET_ENOBUFS 105 // No buffer space available +#define ERROR_INET_EAFNOSUPPORT 106 // EISCONN ? // Address family not supported by protocol family +#define ERROR_INET_EPROTOTYPE 107 // Protocol wrong type for socket +#define ERROR_INET_ENOTSOCK 108 // Socket operation on non-socket +#define ERROR_INET_ENOPROTOOPT 109 // Protocol not available +#define ERROR_INET_ESHUTDOWN 110 // Can't send after socket shutdown +#define ERROR_INET_ECONNREFUSED 111 // Connection refused +#define ERROR_INET_EADDRINUSE 112 // Address already in use +#define ERROR_INET_ECONNABORTED 113 // Connection aborted +#define ERROR_INET_ENETUNREACH 114 // Network is unreachable +#define ERROR_INET_ENETDOWN 115 // Network interface is not configured +#define ERROR_INET_ETIMEDOUT 116 // Connection timed out +#define ERROR_INET_EHOSTDOWN 117 // Host is down +#define ERROR_INET_EHOSTUNREACH 118 // Host is unreachable +#define ERROR_INET_EINPROGRESS 119 // Connection already in progress +#define ERROR_INET_EALREADY 120 // Socket already connected +#define ERROR_INET_EDESTADDRREQ 121 // Destination address required +#define ERROR_INET_EMSGSIZE 122 // Message too long +#define ERROR_INET_EPROTONOSUPPORT 123 // Unknown protocol +#define ERROR_INET_ESOCKTNOSUPPORT 124 // Socket type not supported (linux?) +#define ERROR_INET_EADDRNOTAVAIL 125 // Address not available +#define ERROR_INET_ENETRESET 126 +#define ERROR_INET_EISCONN 127 // Socket is already connected +#define ERROR_INET_ENOTCONN 128 // Socket is not connected +#define ERROR_INET_ETOOMANYREFS 129 +#define ERROR_INET_ENOTSUP 134 // Not supported + +// Maximum queue length specifiable by listen(2) +#define PSP_NET_INET_SOMAXCONN 128 + +// On-Demand Flags +#define PSP_NET_INET_MSG_OOB 0x1 // process out-of-band data +#define PSP_NET_INET_MSG_PEEK 0x2 // peek at incoming message +#define PSP_NET_INET_MSG_DONTROUTE 0x4 // send without using routing tables +#define PSP_NET_INET_MSG_EOR 0x8 // data completes record +#define PSP_NET_INET_MSG_TRUNC 0x10 // data discarded before delivery +#define PSP_NET_INET_MSG_CTRUNC 0x20 // control data lost before delivery +#define PSP_NET_INET_MSG_WAITALL 0x40 // wait for full request or error +#define PSP_NET_INET_MSG_DONTWAIT 0x80 // this message should be nonblocking +#define PSP_NET_INET_MSG_BCAST 0x100 // this message was rcvd using link-level brdcst +#define PSP_NET_INET_MSG_MCAST 0x200 // this message was rcvd using link-level mcast + +// Poll Event Flags (used on events) +#define INET_POLLIN 0x001 // There is data to read. +#define INET_POLLPRI 0x002 // There is urgent data to read. +#define INET_POLLOUT 0x004 // Writing now will not block. + +#define INET_POLLRDNORM 0x040 // Equivalent to POLLIN ? just like _XOPEN_SOURCE? (mapped to read fds_set) +#define INET_POLLWRNORM 0x100 //0x0004 ? // Equivalent to POLLOUT ? just like _XOPEN_SOURCE? (mapped to write fds_set) + +#define INET_POLLRDBAND 0x080 // Priority data may be read. (mapped to exception fds_set) +#define INET_POLLWRBAND 0x200 // Priority data may be written. (mapped to write fds_set?) + +#define INET_POLLERR 0x008 // Error condition. (can appear on revents regardless of events?) +#define INET_POLLHUP 0x010 // Hung up. (can appear on revents regardless of events?) +#define INET_POLLNVAL 0x020 // Invalid polling request. (can appear on revents regardless of events?) + +// Types of socket shutdown(2) +#define PSP_NET_INET_SHUT_RD 0 // Disallow further receives. +#define PSP_NET_INET_SHUT_WR 1 // Disallow further sends. +#define PSP_NET_INET_SHUT_RDWR 2 // Disallow further sends/receives. + +#ifndef SHUT_RD +#define SHUT_RD SD_RECEIVE //0x00 +#endif +#ifndef SHUT_WR +#define SHUT_WR SD_SEND //0x01 +#endif +#ifndef SHUT_RDWR +#define SHUT_RDWR SD_BOTH //0x02 +#endif -// Event Flags -#define INET_POLLRDNORM 0x0040 -#define INET_POLLWRNORM 0x0004 // TODO: Determine how many handlers we can actually have const size_t MAX_APCTL_HANDLERS = 32; @@ -63,6 +285,128 @@ enum { ERROR_NET_CORE_THREAD_BUSY = 0x80410105, ERROR_NET_CORE_80211_NO_BSS = 0x80410106, ERROR_NET_CORE_80211_NO_AVAIL_BSS = 0x80410107, + // from pspsdk's pspnet.h which is similar to https://docs.vitasdk.org/net_2net_8h_source.html + SCE_NET_ERROR_EPERM = 0x80410101, + SCE_NET_ERROR_ENOENT = 0x80410102, + SCE_NET_ERROR_ESRCH = 0x80410103, + SCE_NET_ERROR_EINTR = 0x80410104, + SCE_NET_ERROR_EIO = 0x80410105, + SCE_NET_ERROR_ENXIO = 0x80410106, + SCE_NET_ERROR_E2BIG = 0x80410107, + + SCE_NET_ERROR_ENOEXEC = 0x80410108, + SCE_NET_ERROR_EBADF = 0x80410109, + SCE_NET_ERROR_ECHILD = 0x8041010A, + SCE_NET_ERROR_EDEADLK = 0x8041010B, + SCE_NET_ERROR_ENOMEM = 0x8041010C, + SCE_NET_ERROR_EACCES = 0x8041010D, + SCE_NET_ERROR_EFAULT = 0x8041010E, + SCE_NET_ERROR_ENOTBLK = 0x8041010F, + SCE_NET_ERROR_EBUSY = 0x80410110, + SCE_NET_ERROR_EEXIST = 0x80410111, + SCE_NET_ERROR_EXDEV = 0x80410112, + SCE_NET_ERROR_ENODEV = 0x80410113, + SCE_NET_ERROR_ENOTDIR = 0x80410114, + SCE_NET_ERROR_EISDIR = 0x80410115, + SCE_NET_ERROR_EINVAL = 0x80410116, + SCE_NET_ERROR_ENFILE = 0x80410117, + SCE_NET_ERROR_EMFILE = 0x80410118, + SCE_NET_ERROR_ENOTTY = 0x80410119, + SCE_NET_ERROR_ETXTBSY = 0x8041011A, + SCE_NET_ERROR_EFBIG = 0x8041011B, + SCE_NET_ERROR_ENOSPC = 0x8041011C, + SCE_NET_ERROR_ESPIPE = 0x8041011D, + SCE_NET_ERROR_EROFS = 0x8041011E, + SCE_NET_ERROR_EMLINK = 0x8041011F, + SCE_NET_ERROR_EPIPE = 0x80410120, + SCE_NET_ERROR_EDOM = 0x80410121, + SCE_NET_ERROR_ERANGE = 0x80410122, + SCE_NET_ERROR_EAGAIN = 0x80410123, + SCE_NET_ERROR_EWOULDBLOCK = 0x80410123, + SCE_NET_ERROR_EINPROGRESS = 0x80410124, + SCE_NET_ERROR_EALREADY = 0x80410125, + SCE_NET_ERROR_ENOTSOCK = 0x80410126, + SCE_NET_ERROR_EDESTADDRREQ = 0x80410127, + SCE_NET_ERROR_EMSGSIZE = 0x80410128, + SCE_NET_ERROR_EPROTOTYPE = 0x80410129, + SCE_NET_ERROR_ENOPROTOOPT = 0x8041012A, + SCE_NET_ERROR_EPROTONOSUPPORT = 0x8041012B, + SCE_NET_ERROR_ESOCKTNOSUPPORT = 0x8041012C, + SCE_NET_ERROR_EOPNOTSUPP = 0x8041012D, + SCE_NET_ERROR_EPFNOSUPPORT = 0x8041012E, + SCE_NET_ERROR_EAFNOSUPPORT = 0x8041012F, + SCE_NET_ERROR_EADDRINUSE = 0x80410130, + SCE_NET_ERROR_EADDRNOTAVAIL = 0x80410131, + SCE_NET_ERROR_ENETDOWN = 0x80410132, + SCE_NET_ERROR_ENETUNREACH = 0x80410133, + SCE_NET_ERROR_ENETRESET = 0x80410134, + SCE_NET_ERROR_ECONNABORTED = 0x80410135, + SCE_NET_ERROR_ECONNRESET = 0x80410136, + SCE_NET_ERROR_ENOBUFS = 0x80410137, + SCE_NET_ERROR_EISCONN = 0x80410138, + SCE_NET_ERROR_ENOTCONN = 0x80410139, + SCE_NET_ERROR_ESHUTDOWN = 0x8041013A, + SCE_NET_ERROR_ETOOMANYREFS = 0x8041013B, + SCE_NET_ERROR_ETIMEDOUT = 0x8041013C, + SCE_NET_ERROR_ECONNREFUSED = 0x8041013D, + SCE_NET_ERROR_ELOOP = 0x8041013E, + SCE_NET_ERROR_ENAMETOOLONG = 0x8041013F, + SCE_NET_ERROR_EHOSTDOWN = 0x80410140, + SCE_NET_ERROR_EHOSTUNREACH = 0x80410141, + SCE_NET_ERROR_ENOTEMPTY = 0x80410142, + SCE_NET_ERROR_EPROCLIM = 0x80410143, + SCE_NET_ERROR_EUSERS = 0x80410144, + SCE_NET_ERROR_EDQUOT = 0x80410145, + SCE_NET_ERROR_ESTALE = 0x80410146, + SCE_NET_ERROR_EREMOTE = 0x80410147, + SCE_NET_ERROR_EBADRPC = 0x80410148, + SCE_NET_ERROR_ERPCMISMATCH = 0x80410149, + SCE_NET_ERROR_EPROGUNAVAIL = 0x8041014A, + SCE_NET_ERROR_EPROGMISMATCH = 0x8041014B, + SCE_NET_ERROR_EPROCUNAVAIL = 0x8041014C, + SCE_NET_ERROR_ENOLCK = 0x8041014D, + SCE_NET_ERROR_ENOSYS = 0x8041014E, + SCE_NET_ERROR_EFTYPE = 0x8041014F, + SCE_NET_ERROR_EAUTH = 0x80410150, + SCE_NET_ERROR_ENEEDAUTH = 0x80410151, + SCE_NET_ERROR_EIDRM = 0x80410152, + SCE_NET_ERROR_ENOMS = 0x80410153, + SCE_NET_ERROR_EOVERFLOW = 0x80410154, + SCE_NET_ERROR_EILSEQ = 0x80410155, + SCE_NET_ERROR_ENOTSUP = 0x80410156, + SCE_NET_ERROR_ECANCELED = 0x80410157, + SCE_NET_ERROR_EBADMSG = 0x80410158, + SCE_NET_ERROR_ENODATA = 0x80410159, + SCE_NET_ERROR_ENOSR = 0x8041015A, + SCE_NET_ERROR_ENOSTR = 0x8041015B, + SCE_NET_ERROR_ETIME = 0x8041015C, + + SCE_NET_ERROR_EADHOC = 0x804101A0, + SCE_NET_ERROR_EDISABLEDIF = 0x804101A1, + SCE_NET_ERROR_ERESUME = 0x804101A2, + + SCE_NET_ERROR_ENOTINIT = 0x804101C8, + SCE_NET_ERROR_ENOLIBMEM = 0x804101C9, + SCE_NET_ERROR_ERESERVED202 = 0x804101CA, + SCE_NET_ERROR_ECALLBACK = 0x804101CB, + SCE_NET_ERROR_EINTERNAL = 0x804101CC, + SCE_NET_ERROR_ERETURN = 0x804101CD, + + SCE_NET_ERROR_RESOLVER_EINTERNAL = 0x804101DC, + SCE_NET_ERROR_RESOLVER_EBUSY = 0x804101DD, + SCE_NET_ERROR_RESOLVER_ENOSPACE = 0x804101DE, + SCE_NET_ERROR_RESOLVER_EPACKET = 0x804101DF, + SCE_NET_ERROR_RESOLVER_ERESERVED22 = 0x804101E0, + SCE_NET_ERROR_RESOLVER_ENODNS = 0x804101E1, + SCE_NET_ERROR_RESOLVER_ETIMEDOUT = 0x804101E2, + SCE_NET_ERROR_RESOLVER_ENOSUPPORT = 0x804101E3, + SCE_NET_ERROR_RESOLVER_EFORMAT = 0x804101E4, + SCE_NET_ERROR_RESOLVER_ESERVERFAILURE = 0x804101E5, + SCE_NET_ERROR_RESOLVER_ENOHOST = 0x804101E6, + SCE_NET_ERROR_RESOLVER_ENOTIMPLEMENTED = 0x804101E7, + SCE_NET_ERROR_RESOLVER_ESERVERREFUSED = 0x804101E8, + SCE_NET_ERROR_RESOLVER_ENORECORD = 0x804101E9, + SCE_NET_ERROR_RESOLVER_EALIGNMENT = 0x804101EA, // pspnet_inet ERROR_NET_INET_ALREADY_INITIALIZED = 0x80410201, @@ -127,7 +471,7 @@ enum { ERROR_NET_APCTL_INVALID_CODE = 0x80410a02, ERROR_NET_APCTL_INVALID_IP = 0x80410a03, ERROR_NET_APCTL_NOT_DISCONNECTED = 0x80410a04, - ERROR_NET_APCTL_NOT_IN_BSS = 0x80410a05, + ERROR_NET_APCTL_NOT_IN_BSS = 0x80410a05, // not connected to an AP? ERROR_NET_APCTL_WLAN_SWITCH_OFF = 0x80410a06, ERROR_NET_APCTL_WLAN_BEACON_LOST = 0x80410a07, ERROR_NET_APCTL_WLAN_DISASSOCIATION = 0x80410a08, @@ -135,6 +479,8 @@ enum { ERROR_NET_APCTL_WLAN_SUSPENDED = 0x80410a0a, ERROR_NET_APCTL_TIMEOUT = 0x80410a0b, + ERROR_NET_APCTL_NOT_INITIALIZED = 0x80410a0d, + // wlan errors ERROR_NET_WLAN_ALREADY_JOINED = 0x80410d01, ERROR_NET_WLAN_TRY_JOIN = 0x80410d02, @@ -161,13 +507,13 @@ enum { }; enum { - PSP_NET_APCTL_STATE_DISCONNECTED = 0, - PSP_NET_APCTL_STATE_SCANNING = 1, - PSP_NET_APCTL_STATE_JOINING = 2, - PSP_NET_APCTL_STATE_GETTING_IP = 3, - PSP_NET_APCTL_STATE_GOT_IP = 4, - PSP_NET_APCTL_STATE_EAP_AUTH = 5, - PSP_NET_APCTL_STATE_KEY_EXCHANGE = 6 + PSP_NET_APCTL_STATE_DISCONNECTED = 0, + PSP_NET_APCTL_STATE_SCANNING = 1, + PSP_NET_APCTL_STATE_JOINING = 2, + PSP_NET_APCTL_STATE_GETTING_IP = 3, + PSP_NET_APCTL_STATE_GOT_IP = 4, + PSP_NET_APCTL_STATE_EAP_AUTH = 5, + PSP_NET_APCTL_STATE_KEY_EXCHANGE = 6 }; enum { @@ -218,25 +564,83 @@ enum { #define PSP_NET_APCTL_DESC_SIGNAL_STRENGTH 4 #define PSP_NET_APCTL_DESC_SECURITY 5 +// Similar to https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/fd_set.h +#define PSP_NET_INET_FD_SETSIZE 256 // PSP can support upto 256 fd(s) while the default FD_SETSIZE on Windows is only 64 +#define PSP_NET_INET_NFDBITS 32 // Default: 32 = sizeof(u32) * 8 (8-bits of each byte) = number of bits for each element in fds_bits +#define PSP_NET_INET_NFDBITS_SHIFT 5 // 2^5 = 32 +#define PSP_NET_INET_NFDBITS_MASK 0x1F // 0x1F = 5 bit mask (NFDBITS - 1) +#define PSP_NET_INET_FD_MASK 0xFF // Making sure FD value in the range of 0-255 to prevent out-of-bound + +#define NetInetFD_SET(n, p) \ + ((p)->fds_bits[((n) & PSP_NET_INET_FD_MASK)>>PSP_NET_INET_NFDBITS_SHIFT] |= (1 << ((n) & PSP_NET_INET_NFDBITS_MASK))) // (1 << ((n) % PSP_NET_INET_NFDBITS)) +#define NetInetFD_CLR(n, p) \ + ((p)->fds_bits[((n) & PSP_NET_INET_FD_MASK)>>PSP_NET_INET_NFDBITS_SHIFT] &= ~(1 << ((n) & PSP_NET_INET_NFDBITS_MASK))) +#define NetInetFD_ISSET(n, p) \ + ((p)->fds_bits[((n) & PSP_NET_INET_FD_MASK)>>PSP_NET_INET_NFDBITS_SHIFT] & (1 << ((n) & PSP_NET_INET_NFDBITS_MASK))) +#define NetInetFD_ZERO(p) \ + (void)memset((p), 0, sizeof(*(p))) + +// There might be padding (padded to 4 bytes) between each cmsghdr? Similar to http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch14lev1sec6.html#ch14lev1sec6 +struct InetCmsghdr { + s32_le cmsg_len; // length in bytes, including this structure, includes padding between cmsg_type and cmsg_data[] ? + s32_le cmsg_level; // originating protocol + s32_le cmsg_type; // protocol-specific type + // followed by unsigned char cmsg_data[], there might be 4-bytes padding between cmsg_type and cmsg_data[] ? +}; + +struct SceNetInetTimeval { + u32_le tv_sec; // seconds + u32_le tv_usec; // and microseconds +}; + #ifdef _MSC_VER #pragma pack(push,1) #endif + +// FdSet +typedef struct SceNetInetFdSet { + u32_le fds_bits[(PSP_NET_INET_FD_SETSIZE+(PSP_NET_INET_NFDBITS-1)) / PSP_NET_INET_NFDBITS]; // Default: 8 = ((PSP_NET_INET_FD_SETSIZE(256) + (PSP_NET_INET_NFDBITS-1)) / PSP_NET_INET_NFDBITS(32)) elements of 32-bit array to represents 256(FD_SETSIZE) bits of fd's bitmap +} PACK SceNetInetFdSet; + // Sockaddr typedef struct SceNetInetSockaddr { - uint8_t sa_len; + uint8_t sa_len; // length of this struct or sa_data only? uint8_t sa_family; - uint8_t sa_data[14]; + uint8_t sa_data[14]; // up to 14 bytes of data? } PACK SceNetInetSockaddr; // Sockaddr_in typedef struct SceNetInetSockaddrIn { - uint8_t sin_len; + uint8_t sin_len; // length of this struct? uint8_t sin_family; u16_le sin_port; //uint16_t u32_le sin_addr; //uint32_t - uint8_t sin_zero[8]; + uint8_t sin_zero[8]; // zero-filled padding? } PACK SceNetInetSockaddrIn; +// Similar to iovec struct on 32-bit platform from BSD's uio.h/_iovec.h +typedef struct SceNetIovec { + u32_le iov_base; // Base address (pointer/void* of buffer) + u32_le iov_len; // Length +} PACK SceNetIovec; + +// Similar to msghdr struct on 32-bit platform from BSD's socket.h +typedef struct InetMsghdr { + u32_le msg_name; // optional address (pointer/void* to sockaddr_in/SceNetInetSockaddrIn/SceNetInetSockaddr struct?) + u32_le msg_namelen; // size of optional address + u32_le msg_iov; // pointer to iovec/SceNetIovec (ie. struct iovec*/PSPPointer), scatter/gather array (buffers are concatenated before sent?) + s32_le msg_iovlen; // # elements in msg_iov + u32_le msg_control; // pointer (ie. void*/PSPPointer) to ancillary data (multiple of cmsghdr/InetCmsghdr struct?), see below + u32_le msg_controllen; // ancillary data buffer len, includes padding between each cmsghdr/InetCmsghdr struct? + s32_le msg_flags; // flags on received message (ignored on sendmsg?) +} PACK InetMsgHdr; + +// Structure used for manipulating linger option +typedef struct SceNetInetLinger { + s32_le l_onoff; // option on/off + s32_le l_linger; // linger time in seconds +} PACK SceNetInetLinger; + // Polling Event Field typedef struct SceNetInetPollfd { //similar format to pollfd in 32bit (pollfd in 64bit have different size) s32_le fd; @@ -249,6 +653,17 @@ typedef struct ProductStruct { // Similar to SceNetAdhocctlAdhocId ? char product[PRODUCT_CODE_LENGTH]; // Game ID (Example: ULUS10000) } PACK ProductStruct; +// TCP & UDP Socket Union (Internal use only) +typedef struct InetSocket { + s32_le id; // posix socket id + s32_le domain; // AF_INET/PF_INET/etc + s32_le type; // SOCK_STREAM/SOCK_DGRAM/etc + s32_le protocol; // TCP/UDP/etc + s32_le nonblocking; // non-blocking flag (ie. FIONBIO) to keep track of the blocking mode since Windows doesn't have getter for this + s32_le so_broadcast; // broadcast flag (ie. SO_BROADCAST) to keep track of the broadcast flag, since we're using fake broadcast + s32_le tcp_state; // to keep track TCP connection state +} PACK InetSocket; + #ifdef _MSC_VER #pragma pack(pop) #endif @@ -290,6 +705,63 @@ struct ApctlArgs { u32_le data[5]; // OldState, NewState, Event, Error, ArgsAddr }; +struct NetResolver { + int id; + bool isRunning; + u32 bufferAddr; // May be used for the Async version? + int bufferLen; +}; + +// ----------------- DNS Header ----------------------------- +// Based on https://web.archive.org/web/20201204080751/https://www.binarytides.com/dns-query-code-in-c-with-winsock/ +typedef struct +{ + unsigned short id; // identification number + unsigned char rd : 1; // recursion desired + unsigned char tc : 1; // truncated message + unsigned char aa : 1; // authoritive answer + unsigned char opcode : 4; // purpose of message + unsigned char qr : 1; // query/response flag + unsigned char rcode : 4; // response code + unsigned char cd : 1; // checking disabled + unsigned char ad : 1; // authenticated data + unsigned char z : 1; // its z! reserved + unsigned char ra : 1; // recursion available + unsigned short q_count; // number of question entries + unsigned short ans_count; // number of answer entries + unsigned short auth_count; // number of authority entries + unsigned short add_count; // number of resource entries +} DNS_HEADER; + +typedef struct +{ + unsigned short qtype; + unsigned short qclass; +} QUESTION; + +typedef struct +{ + unsigned short type; + unsigned short _class; + unsigned int ttl; + unsigned short data_len; +} R_DATA; + +typedef struct +{ + unsigned char* name; + R_DATA* resource; + unsigned char* rdata; +} RES_RECORD; + +typedef struct +{ + unsigned char* name; + QUESTION* ques; +} QUERY; +// --------------------------------------------------------- + + class PointerWrap; class AfterApctlMipsCall : public PSPAction { @@ -314,6 +786,8 @@ extern bool netInetInited; extern bool netApctlInited; extern u32 netApctlState; extern SceNetApctlInfoInternal netApctlInfo; +extern std::string defaultNetConfigName; +extern std::string defaultNetSSID; template std::string num2hex(I w, size_t hex_len = sizeof(I) << 1); std::string error2str(u32 errorcode); @@ -322,6 +796,7 @@ void Register_sceNet(); void Register_sceWlanDrv(); void Register_sceNetUpnp(); void Register_sceNetIfhandle(); +void Register_sceNetApctl_internal_user(); void __NetInit(); @@ -334,3 +809,4 @@ int sceNetApctlConnect(int connIndex); int sceNetInetPoll(void *fds, u32 nfds, int timeout); int sceNetInetTerm(); int sceNetApctlTerm(); +int sceNetApctlDisconnect(); diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 0441b6f3729c..183dcd626729 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -86,6 +86,7 @@ int adhocDefaultDelay = 10000; //10000 int adhocExtraDelay = 20000; //20000 int adhocEventPollDelay = 100000; //100000; // Same timings with PSP_ADHOCCTL_RECV_TIMEOUT ? int adhocMatchingEventDelay = 30000; //30000 +int adhocMatchingDefaultDelay = 2000; //10000 int adhocEventDelay = 2000000; //2000000 on real PSP ? u32 defaultLastRecvDelta = 10000; //10000 usec worked well for games published by Falcom (ie. Ys vs Sora Kiseki, Vantage Master Portable) @@ -112,6 +113,7 @@ u32_le dummyThreadCode[3]; u32 matchingThreadHackAddr = 0; u32_le matchingThreadCode[3]; +void sendBulkDataPacket(SceNetAdhocMatchingContext* context, SceNetEtherAddr* mac, int datalen, void* data); int matchingEventThread(int matchingId); int matchingInputThread(int matchingId); void sendBulkDataPacket(SceNetAdhocMatchingContext* context, SceNetEtherAddr* mac, int datalen, void* data); @@ -360,6 +362,7 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { sockerr = EAGAIN; // Don't send anything yet if connection to Adhoc Server is still in progress if (!isAdhocctlNeedLogin && IsSocketReady((int)metasocket, false, true) > 0) { + DEBUG_LOG(SCENET, "sceNetAdhocctl[%i]: Sending OPCODE %d", uid, req.opcode); ret = send((int)metasocket, (const char*)&packet, len, MSG_NOSIGNAL); sockerr = errno; // Successfully Sent or Connection has been closed or Connection failure occurred @@ -376,12 +379,12 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { if ((req.opcode == OPCODE_LOGIN && !networkInited) || (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK))) { u64 now = (u64)(time_now_d() * 1000000.0); if (now - adhocctlStartTime <= static_cast(adhocDefaultTimeout) + 500) { - // Try again in another 0.5ms until timedout. + // Try again in another 0.5ms until timedout CoreTiming::ScheduleEvent(usToCycles(500) - cyclesLate, adhocctlNotifyEvent, userdata); return; } else if (req.opcode != OPCODE_LOGIN) - result = ERROR_NET_ADHOCCTL_BUSY; + result = ERROR_NET_ADHOCCTL_TIMEOUT; // FIXME: We should probably need to passed the error code through Adhocctl Handler's ERROR Event when a handler existed } } else @@ -1282,25 +1285,31 @@ void __NetAdhocInit() { } u32 sceNetAdhocInit() { - if (!netAdhocInited) { - // Library initialized - netAdhocInited = true; - isAdhocctlBusy = false; - - // FIXME: It seems official prx is using sceNetAdhocGameModeDeleteMaster in here? - NetAdhocGameMode_DeleteMaster(); - // Since we are deleting GameMode Master here, we should probably need to make sure GameMode resources all cleared too. - deleteAllGMB(); + // FIXME: If Net library is not inited return 0x800201ca ? + if (!netInited) + return hleLogError(SCENET, SCE_KERNEL_ERROR_LWMUTEX_NOT_FOUND, "net was not initialized?"); - // Return Success - return hleLogSuccessInfoI(SCENET, 0, "at %08x", currentMIPS->pc); - } // Already initialized - return hleLogWarning(SCENET, ERROR_NET_ADHOC_ALREADY_INITIALIZED, "already initialized"); + if (netAdhocInited) + return hleLogError(SCENET, ERROR_NET_ADHOC_ALREADY_INITIALIZED, "already initialized"); + + netAdhocInited = true; + isAdhocctlBusy = false; + + // FIXME: It seems official prx is using sceNetAdhocGameModeDeleteMaster in here? + NetAdhocGameMode_DeleteMaster(); + // Since we are deleting GameMode Master here, we should probably need to make sure GameMode resources all cleared too. + deleteAllGMB(); + + // Return Success + return hleLogSuccessInfoI(SCENET, 0, "at %08x", currentMIPS->pc); } -static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { +int sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { INFO_LOG(SCENET, "sceNetAdhocctlInit(%i, %i, %08x) at %08x", stackSize, prio, productAddr, currentMIPS->pc); + if (!g_Config.bEnableWlan) { + return -1; + } // FIXME: Returning 0x8002013a (SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED) without adhoc module loaded first? // FIXME: Sometimes returning 0x80410601 (ERROR_NET_ADHOC_AUTH_ALREADY_INITIALIZED / Library module is already initialized ?) when AdhocctlTerm is not fully done? @@ -1446,9 +1455,10 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 f if (iResult == 0) { // Workaround: Send a dummy 0 size message to AdhocServer IP to make sure the socket actually bound to an address when binded with INADDR_ANY before using getsockname, seems to fix sending from incorrect port issue on MGS:PW on Android - addr.sin_addr.s_addr = g_adhocServerIP.in.sin_addr.s_addr; + /*addr.sin_addr.s_addr = g_adhocServerIP.in.sin_addr.s_addr; addr.sin_port = 0; sendto(usocket, dummyPeekBuf64k, 0, MSG_NOSIGNAL, (struct sockaddr*)&addr, sizeof(addr)); + */ // Update sport with the port assigned internal->lport = ntohs(local.sin_port) socklen_t len = sizeof(addr); if (getsockname(usocket, (struct sockaddr*)&addr, &len) == 0) { @@ -1542,7 +1552,7 @@ static int sceNetAdhocctlGetParameter(u32 paramAddr) { memcpy(grpName, parameter.group_name.data, ADHOCCTL_GROUPNAME_LEN); parameter.nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; DEBUG_LOG(SCENET, "sceNetAdhocctlGetParameter(%08x) [Ch=%i][Group=%s][BSSID=%s][name=%s]", paramAddr, parameter.channel, grpName, mac2str(¶meter.bssid.mac_addr).c_str(), parameter.nickname.data); - if (!g_Config.bEnableWlan) { + if (!g_Config.bEnableWlan || adhocctlState == ADHOCCTL_STATE_DISCONNECTED) { return ERROR_NET_ADHOCCTL_DISCONNECTED; } @@ -2556,7 +2566,7 @@ u32 NetAdhocctl_Disconnect() { return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; } -static u32 sceNetAdhocctlDisconnect() { +int sceNetAdhocctlDisconnect() { // WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right? char grpName[9] = { 0 }; memcpy(grpName, parameter.group_name.data, ADHOCCTL_GROUPNAME_LEN); // Copied to null-terminated var to prevent unexpected behaviour on Logs @@ -2781,6 +2791,13 @@ int NetAdhocctl_Create(const char* groupName) { if (netAdhocctlInited) { // Valid Argument if (validNetworkName(groupNameStruct)) { + // FIXME: If Net library is not inited, return success but with Error 0x80410180 through Adhocctl Handler + if (!netInited) { + notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, 0x80410180); // library/address not available? + hleEatMicro(500); + return 0; + } + // FIXME: When tested with JPCSP + official prx files it seems when adhocctl in a connected state (ie. joined to a group) attempting to create/connect/join/scan will return a success (without doing anything?) if ((adhocctlState == ADHOCCTL_STATE_CONNECTED) || (adhocctlState == ADHOCCTL_STATE_GAMEMODE)) { // TODO: Need to test this on games that doesn't use Adhocctl Handler too (not sure if there are games like that tho) @@ -3068,6 +3085,7 @@ int sceNetAdhocTerm() { // WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup all the sockets right? int retval = NetAdhoc_Term(); + // Gives enough time for AdhocServer to remove the player before the next Init to avoid "Already Existing IP" issue hleEatMicro(adhocDefaultDelay); return hleLogSuccessInfoI(SCENET, retval); } @@ -4842,7 +4860,9 @@ int sceNetAdhocMatchingInit(u32 memsize) { return ERROR_NET_ADHOC_MATCHING_ALREADY_INITIALIZED; // Save Fake Pool Size - fakePoolSize = memsize; + netAdhocPoolStat.pool = memsize - 0x20; // Slightly (32 bytes) smaller than what passed as init argument + netAdhocPoolStat.maximum = 0x4050; // Dummy maximum foot print ever allocated? (0 right after init) + netAdhocPoolStat.free = netAdhocPoolStat.pool; // Dummy free size (same as pool right after init), we should set this high enough to prevent any issue // Initialize Library deleteMatchingEvents(); @@ -4893,11 +4913,11 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle // Library initialized if (netAdhocMatchingInited) { // Valid Member Limit - if (maxnum > 1 && maxnum <= 16) { + if (maxnum > 1 && maxnum <= PSP_NET_ADHOC_MATCHING_MAXNUM) { // Valid Receive Buffer size if (rxbuflen >= 1) { //1024 //200 on DBZ Shin Budokai 2 // Valid Arguments - if (mode >= 1 && mode <= 3) { + if (mode >= 1 && mode <= PSP_ADHOC_MATCHING_MODE_PTP) { // Iterate Matching Contexts SceNetAdhocMatchingContext * item = contexts; @@ -4998,6 +5018,10 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle } int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int evthStack, int inthPri, int inthPartitionId, int inthStack, int optLen, u32 optDataAddr) { + // Invalid Optional Data Length + if (optLen > 0 && (optDataAddr == 0 || optLen > PSP_NET_ADHOC_MATCHING_MAXHELLOOPTLEN)) + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN, "adhocmatching invalid optlen"); + // Multithreading Lock peerlock.lock(); @@ -5114,7 +5138,7 @@ static int sceNetAdhocMatchingSelectTarget(int matchingId, const char *macAddres if (peer != NULL) { // Valid Optional Data Length - if ((optLen == 0) || (optLen > 0 && optDataPtr != 0)) + if ((optLen == 0) || (optLen > 0 && optDataPtr != 0 && optLen <= PSP_NET_ADHOC_MATCHING_MAXOPTLEN)) { void * opt = NULL; if (Memory::IsValidAddress(optDataPtr)) opt = Memory::GetPointerWriteUnchecked(optDataPtr); @@ -5249,6 +5273,10 @@ int NetAdhocMatching_CancelTargetWithOpt(int matchingId, const char* macAddress, // Valid Arguments if (target != NULL && ((optLen == 0) || (optLen > 0 && opt != NULL))) { + // Invalid Optional Data Length + if (optLen > PSP_NET_ADHOC_MATCHING_MAXOPTLEN) + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN, "adhocmatching invalid optlen"); + // Find Matching Context SceNetAdhocMatchingContext* context = findMatchingContext(matchingId); @@ -5324,7 +5352,7 @@ int sceNetAdhocMatchingCancelTargetWithOpt(int matchingId, const char *macAddres } int sceNetAdhocMatchingCancelTarget(int matchingId, const char *macAddress) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingCancelTarget(%i, %s)", matchingId, mac2str((SceNetEtherAddr*)macAddress).c_str()); + WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingCancelTarget(%i, %s) at %08x", matchingId, mac2str((SceNetEtherAddr*)macAddress).c_str(), currentMIPS->pc); if (!g_Config.bEnableWlan) return -1; return NetAdhocMatching_CancelTargetWithOpt(matchingId, macAddress, 0, 0); @@ -5390,7 +5418,7 @@ int sceNetAdhocMatchingSetHelloOpt(int matchingId, int optLenAddr, u32 optDataAd return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_RUNNING, "adhocmatching not running"); // Invalid Optional Data Length - if ((optLenAddr != 0) && (optDataAddr == 0)) + if (Memory::IsValidAddress(optLenAddr) && (optDataAddr == 0 || Memory::Read_U32(optLenAddr) > PSP_NET_ADHOC_MATCHING_MAXHELLOOPTLEN)) return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN, "adhocmatching invalid optlen"); //ERROR_NET_ADHOC_MATCHING_INVALID_ARG // Grab Existing Hello Data @@ -5632,6 +5660,12 @@ static int sceNetAdhocMatchingGetMembers(int matchingId, u32 sizeAddr, u32 buf) DEBUG_LOG(SCENET, "MemberList [Requested: %i][Discovered: %i]", requestedpeers, filledpeers); } + static int prev_members = 0; + if (*buflen < prev_members) { + WARN_LOG(SCENET, "MemberList: Prev (%u) ==> Current (%u)", prev_members / sizeof(SceNetAdhocMatchingMemberInfoEmu), *buflen / sizeof(SceNetAdhocMatchingMemberInfoEmu)); + } + prev_members = *buflen; + // Return Success return hleDelayResult(0, "delay 100 ~ 1000us", 100); // seems to have different thread running within the delay duration } @@ -5781,8 +5815,8 @@ static int sceNetAdhocMatchingGetPoolMaxAlloc() { if (!g_Config.bEnableWlan) return -1; - // Lazy way out - hardcoded return value - return hleLogDebug(SCENET, fakePoolSize/2, "faked value"); + // max footprint ever allocated? + return hleLogDebug(SCENET, netAdhocPoolStat.maximum, "faked value"); } int sceNetAdhocMatchingGetPoolStat(u32 poolstatPtr) { @@ -5790,30 +5824,19 @@ int sceNetAdhocMatchingGetPoolStat(u32 poolstatPtr) { if (!g_Config.bEnableWlan) return -1; - // Initialized Library - if (netAdhocMatchingInited) - { - SceNetMallocStat * poolstat = NULL; - if (Memory::IsValidAddress(poolstatPtr)) poolstat = (SceNetMallocStat *)Memory::GetPointer(poolstatPtr); - - // Valid Argument - if (poolstat != NULL) - { - // Fill Poolstat with Fake Data - poolstat->pool = fakePoolSize; - poolstat->maximum = fakePoolSize / 2; // Max usage faked to halt the pool - poolstat->free = fakePoolSize - poolstat->maximum; - - // Return Success - return 0; - } + // Uninitialized Library + if (!netAdhocMatchingInited) + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhocmatching not initialized"); - // Invalid Argument + auto poolstat = PSPPointer::Create(poolstatPtr); + if (!poolstat.IsValid()) return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ARG, "adhocmatching invalid arg"); - } - // Uninitialized Library - return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhocmatching not initialized"); + *poolstat = netAdhocPoolStat; + poolstat.NotifyWrite("NetAdhocMatchingGetPoolStat"); + + // Return Success + return 0; } void __NetTriggerCallbacks() @@ -5856,7 +5879,7 @@ void __NetTriggerCallbacks() break; case ADHOCCTL_EVENT_DISCONNECT: newState = ADHOCCTL_STATE_DISCONNECTED; - delayus = adhocDefaultDelay; // Tekken 5 expects AdhocctlDisconnect to be done within ~17ms (a frame?) + delayus = adhocDefaultDelay / 2; // Tekken 5 expects AdhocctlDisconnect to be done within ~17ms (a frame time?), but might be safer to be less than a frame time break; case ADHOCCTL_EVENT_GAME: { @@ -5915,27 +5938,45 @@ void __NetMatchingCallbacks() //(int matchingId) int delayus = 3000; auto params = matchingEvents.begin(); - if (params != matchingEvents.end()) { + if (params != matchingEvents.end()) + { u32_le args[6]; memcpy(args, params->data, sizeof(args)); - auto context = findMatchingContext(args[0]); + auto& ctxid = args[0]; + auto& eventid = args[1]; + auto& bufAddr = args[2]; + auto& optlen = args[3]; + auto& optDataAddr = args[4]; + auto& entryPoint = args[5]; + auto context = findMatchingContext(ctxid); if (actionAfterMatchingMipsCall < 0) { actionAfterMatchingMipsCall = __KernelRegisterActionType(AfterMatchingMipsCall::Create); } + + //delayus = adhocMatchingEventDelay; // Add extra delay to prevent I/O Timing method from causing disconnection, but delaying too long may cause matchingEvents to pile up + switch (eventid) { + case PSP_ADHOC_MATCHING_EVENT_ESTABLISHED: + delayus = adhocMatchingDefaultDelay; // Must not takes too long otherwise Def Jam Fight for NY will think the join request got declined + break; + case PSP_ADHOC_MATCHING_EVENT_DATA_ACK: + delayus = adhocMatchingDefaultDelay; // Must not takes too long + break; + } + DEBUG_LOG(SCENET, "AdhocMatching - Remaining Events: %zu", matchingEvents.size()); - auto peer = findPeer(context, (SceNetEtherAddr*)Memory::GetPointer(args[2])); + auto peer = findPeer(context, (SceNetEtherAddr*)Memory::GetPointer(bufAddr)); // Discard HELLO Events when in the middle of joining, as some games (ie. Super Pocket Tennis) might tried to join again (TODO: Need to confirm whether sceNetAdhocMatchingSelectTarget supposed to be blocking the current thread or not) - if (peer == NULL || (args[1] != PSP_ADHOC_MATCHING_EVENT_HELLO || (peer->state != PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST && peer->state != PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST))) { - DEBUG_LOG(SCENET, "AdhocMatchingCallback: [ID=%i][EVENT=%i][%s]", args[0], args[1], mac2str((SceNetEtherAddr *)Memory::GetPointer(args[2])).c_str()); + if (peer == NULL || (eventid != PSP_ADHOC_MATCHING_EVENT_HELLO || (peer->state != PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST && peer->state != PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST))) { + DEBUG_LOG(SCENET, "AdhocMatchingCallback: [ID=%i][EVENT=%i][%s]", ctxid, eventid, mac2str((SceNetEtherAddr *)Memory::GetPointer(bufAddr)).c_str()); AfterMatchingMipsCall* after = (AfterMatchingMipsCall*)__KernelCreateAction(actionAfterMatchingMipsCall); - after->SetData(args[0], args[1], args[2]); - hleEnqueueCall(args[5], 5, args, after); + after->SetData(ctxid, eventid, bufAddr); + hleEnqueueCall(entryPoint, 5, args, after); matchingEvents.pop_front(); } else { - DEBUG_LOG(SCENET, "AdhocMatching - Discarding Callback: [ID=%i][EVENT=%i][%s]", args[0], args[1], mac2str((SceNetEtherAddr*)Memory::GetPointer(args[2])).c_str()); + DEBUG_LOG(SCENET, "AdhocMatching - Discarding Callback: [ID=%i][EVENT=%i][%s]", ctxid, eventid, mac2str((SceNetEtherAddr*)Memory::GetPointer(bufAddr)).c_str()); matchingEvents.pop_front(); } } @@ -6119,6 +6160,12 @@ static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) { DEBUG_LOG(SCENET, "PeerList [Requested: %i][Discovered: %i]", requestcount, discovered); } + static int prev_peers = 0; + if (*buflen < prev_peers) { + WARN_LOG(SCENET, "PeerList: Prev (%u) ==> Current (%u)", prev_peers / sizeof(SceNetAdhocctlPeerInfoEmu), *buflen / sizeof(SceNetAdhocctlPeerInfoEmu)); + } + prev_peers = *buflen; + // Multithreading Unlock peerlock.unlock(); @@ -6252,11 +6299,11 @@ static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 b } const HLEFunction sceNetAdhocctl[] = { - {0XE26F226E, &WrapU_IIU, "sceNetAdhocctlInit", 'x', "iix" }, + {0XE26F226E, &WrapI_IIU, "sceNetAdhocctlInit", 'i', "iix" }, {0X9D689E13, &WrapI_V, "sceNetAdhocctlTerm", 'i', "" }, {0X20B317A0, &WrapU_UU, "sceNetAdhocctlAddHandler", 'x', "xx" }, {0X6402490B, &WrapU_U, "sceNetAdhocctlDelHandler", 'x', "x" }, - {0X34401D65, &WrapU_V, "sceNetAdhocctlDisconnect", 'x', "" }, + {0X34401D65, &WrapI_V, "sceNetAdhocctlDisconnect", 'i', "" }, {0X0AD043ED, &WrapI_C, "sceNetAdhocctlConnect", 'i', "s" }, {0X08FFF7A0, &WrapI_V, "sceNetAdhocctlScan", 'i', "" }, {0X75ECD386, &WrapI_U, "sceNetAdhocctlGetState", 'i', "x" }, diff --git a/Core/HLE/sceNetAdhoc.h b/Core/HLE/sceNetAdhoc.h index 1ea5421980eb..dbe5f7df4e70 100644 --- a/Core/HLE/sceNetAdhoc.h +++ b/Core/HLE/sceNetAdhoc.h @@ -113,6 +113,8 @@ int sceNetAdhocctlConnect(const char* groupName); int sceNetAdhocctlJoin(u32 scanInfoAddr); int sceNetAdhocctlScan(); int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr); +int sceNetAdhocctlDisconnect(); +int sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr); int NetAdhocMatching_Term(); int NetAdhocctl_Term(); diff --git a/Core/HLE/sceNp.cpp b/Core/HLE/sceNp.cpp index c5ca4be4c6b0..5394c7d892bd 100644 --- a/Core/HLE/sceNp.cpp +++ b/Core/HLE/sceNp.cpp @@ -336,10 +336,10 @@ int sceNpAuthCreateStartRequest(u32 paramAddr) npServiceId = Memory::GetCharPointer(params.serviceIdAddr); INFO_LOG(SCENET, "%s - Max Version: %u.%u", __FUNCTION__, params.version.major, params.version.minor); - INFO_LOG(SCENET, "%s - Service ID: %s", __FUNCTION__, Memory::GetCharPointer(params.serviceIdAddr)); - INFO_LOG(SCENET, "%s - Entitlement ID: %s", __FUNCTION__, Memory::GetCharPointer(params.entitlementIdAddr)); + INFO_LOG(SCENET, "%s - Service ID: %s", __FUNCTION__, safe_string(Memory::GetCharPointer(params.serviceIdAddr))); + INFO_LOG(SCENET, "%s - Entitlement ID: %s", __FUNCTION__, safe_string(Memory::GetCharPointer(params.entitlementIdAddr))); INFO_LOG(SCENET, "%s - Consumed Count: %d", __FUNCTION__, params.consumedCount); - INFO_LOG(SCENET, "%s - Cookie (size = %d): %s", __FUNCTION__, params.cookieSize, Memory::GetCharPointer(params.cookieAddr)); + INFO_LOG(SCENET, "%s - Cookie (size = %d): %s", __FUNCTION__, params.cookieSize, safe_string(Memory::GetCharPointer(params.cookieAddr))); u32 retval = 0; if (params.size >= 32 && params.ticketCbAddr != 0) { @@ -442,7 +442,7 @@ int sceNpAuthGetTicket(u32 requestId, u32 bufferAddr, u32 length) int sceNpAuthGetEntitlementById(u32 ticketBufferAddr, u32 ticketLength, u32 entitlementIdAddr, u32 arg4) { ERROR_LOG(SCENET, "UNIMPL %s(%08x, %d, %08x, %d)", __FUNCTION__, ticketBufferAddr, ticketLength, entitlementIdAddr, arg4); - INFO_LOG(SCENET, "%s - Entitlement ID: %s", __FUNCTION__, Memory::GetCharPointer(entitlementIdAddr)); + INFO_LOG(SCENET, "%s - Entitlement ID: %s", __FUNCTION__, safe_string(Memory::GetCharPointer(entitlementIdAddr))); // Do we return the entitlement through function result? or update the ticket content? or replace the arg3 data with SceNpEntitlement struct? return 1; // dummy value assuming it's a boolean/flag, since we don't know how to return the entitlement result yet @@ -540,7 +540,7 @@ static int sceNpServiceInit(u32 poolSize, u32 stackSize, u32 threadPrio) static int sceNpLookupCreateTransactionCtx(u32 lookupTitleCtxIdAddr) { ERROR_LOG(SCENET, "UNIMPL %s(%08x)", __FUNCTION__, lookupTitleCtxIdAddr); - INFO_LOG(SCENET, "%s - Title ID: %s", __FUNCTION__, Memory::GetCharPointer(lookupTitleCtxIdAddr)); + INFO_LOG(SCENET, "%s - Title ID: %s", __FUNCTION__, safe_string(Memory::GetCharPointer(lookupTitleCtxIdAddr))); // Patapon 3 will only Destroy if returned Id > 0. Is 0 a valid id? return 1; // returning dummy transaction id } diff --git a/Core/HLE/sceNp2.cpp b/Core/HLE/sceNp2.cpp index 70b349145bf6..28b6ef0fea76 100644 --- a/Core/HLE/sceNp2.cpp +++ b/Core/HLE/sceNp2.cpp @@ -139,13 +139,14 @@ static int sceNpMatching2ContextStart(int ctxId) client.SetDataTimeout(20.0); if (client.Connect()) { - char requestHeaders[4096]; - snprintf(requestHeaders, sizeof(requestHeaders), - "User-Agent: PS3Community-agent/1.0.0 libhttp/1.0.0\r\n"); + //char requestHeaders[4096]; + //snprintf(requestHeaders, sizeof(requestHeaders), + // "User-Agent: PS3Community-agent/1.0.0 libhttp/1.0.0\r\n"); + client.SetUserAgent("PS3Community-agent/1.0.0 libhttp/1.0.0"); DEBUG_LOG(SCENET, "GET URI: %s", url.ToString().c_str()); http::RequestParams req(url.Resource(), "*/*"); - int err = client.SendRequest("GET", req, requestHeaders, &progress); + int err = client.SendRequest("GET", req, nullptr, &progress); if (err < 0) { client.Disconnect(); return hleLogError(SCENET, SCE_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_TITLE, "HTTP GET Error = %d", err); @@ -405,7 +406,8 @@ static int sceNpMatching2GetServerInfo(int ctxId, u32 serverIdPtr, u32 unknown1P //args.data[9] = 0 or a pointer to a struct related to context and matched serverId? //args.data[10] = serverId; - notifyNpMatching2Handlers(args, ctxId, serverId, 0, 0, 0, 0, 0, 1); + // Note: Temporary commenting the callback notification, as it's still experimental and could crash the game (ie. Fat Princess), the game seems to expects a callback to occurs in order to progress. + //notifyNpMatching2Handlers(args, ctxId, serverId, 0, 0, 0, 0, 0, 1); Memory::Write_U32(args.data[1], unknown2Ptr); // server status or flags? } diff --git a/Core/HLE/sceNp2.h b/Core/HLE/sceNp2.h index 8b1b6c1c567e..bbd00d4523ca 100644 --- a/Core/HLE/sceNp2.h +++ b/Core/HLE/sceNp2.h @@ -251,4 +251,8 @@ struct NpMatching2Args { #pragma pack(pop) +extern std::recursive_mutex npMatching2EvtMtx; +extern std::deque npMatching2Events; +extern std::map npMatching2Handlers; + void Register_sceNpMatching2(); diff --git a/Core/HLE/sceParseHttp.cpp b/Core/HLE/sceParseHttp.cpp index 7da109f4afdf..db3a68d08e82 100644 --- a/Core/HLE/sceParseHttp.cpp +++ b/Core/HLE/sceParseHttp.cpp @@ -15,16 +15,167 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include +#include + #include "Core/HLE/HLE.h" +#include "Core/HLE/sceHttp.h" #include "Core/HLE/sceParseHttp.h" +#include "Core/HLE/FunctionWrappers.h" +#include "Core/Debugger/MemBlockInfo.h" +#include "Common/StringUtils.h" + +std::string getHeaderString(std::istringstream &headers) { + std::string line = ""; + while (headers.rdbuf()->in_avail()) { + char c = headers.get(); + if (c == '\n' || c == '\r') { + break; + } + line.push_back(c); + } + + return line; +} + +// FIXME: Is it allowed for fieldName to be null/0 or empty string ? JPCSP seems to ignore it +static int sceParseHttpResponseHeader(u32 headerAddr, int headerLength, const char *fieldName, u32 valueAddr, u32 valueLengthAddr) { + WARN_LOG(SCENET, "UNTESTED sceParseHttpResponseHeader(%x, %i, %s, %x, %x[%i]) at %08x", headerAddr, headerLength, fieldName, valueAddr, valueLengthAddr, Memory::Read_U32(valueLengthAddr), currentMIPS->pc); + if (!Memory::IsValidRange(headerAddr, headerLength)) + return hleLogError(SCENET, -1, "invalid arg"); + + if (!Memory::IsValidRange(valueLengthAddr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); + + // FIXME: Not sure whether valuerAddr can be null or not (in case the game just need to get the size to allocate the value buffer first) + // Note: Based on the outputted value address from JPCSP, the address seems to be within the input headers address range, thus no need to allocate output value buffer + if (!Memory::IsValidRange(valueAddr, Memory::Read_U32(valueLengthAddr))) + return hleLogError(SCENET, -1, "invalid arg"); + + std::string field = ""; + if (fieldName != nullptr) + field = std::string(fieldName); + field = StripSpaces(field); + std::string headers(Memory::GetCharPointer(headerAddr), headerLength); + std::istringstream hdrs(headers); + u32 addr = 0; + u32 len = 0; + bool found = false; + while (hdrs.rdbuf()->in_avail()) { + std::string headerString = getHeaderString(hdrs); + const std::string delim = ":"; // JPCSP use ": " delimiter + size_t delimpos = headerString.find(delim); + std::string key = headerString.substr(0, delimpos); // the key part + if (equalsNoCase(StripSpaces(key), field.c_str())) { + found = true; + int offset = hdrs.tellg(); + if (offset >= 0) { + offset -= (int)headerString.length(); + offset--; // counting the excluded LF/CR + } + else { + offset = headerLength - (int)headerString.length(); + } + addr = headerAddr + offset + (int)(delimpos != std::string::npos? delimpos + delim.length() : headerString.length()); // value address within the headers + headerString.erase(0, delimpos + delim.length()); // the value part + size_t valpos = headerString.find_first_not_of(" "); // excludes the leading space (if any) + if (valpos == std::string::npos) { + len = 0; + } + else { + len = (int)headerString.length() - (int)valpos; + addr += (int)valpos; + } + + Memory::WriteUnchecked_U32(addr, valueAddr); + NotifyMemInfo(MemBlockFlags::WRITE, valueAddr, 4, "sceParseHttpResponseHeader"); + Memory::WriteUnchecked_U32(len, valueLengthAddr); + NotifyMemInfo(MemBlockFlags::WRITE, valueLengthAddr, 4, "sceParseHttpResponseHeader"); + break; + } + } + + if (!found) { + Memory::WriteUnchecked_U32(0, valueAddr); + NotifyMemInfo(MemBlockFlags::WRITE, valueAddr, 4, "sceParseHttpResponseHeader"); + Memory::WriteUnchecked_U32(0, valueLengthAddr); + NotifyMemInfo(MemBlockFlags::WRITE, valueLengthAddr, 4, "sceParseHttpResponseHeader"); + return hleLogError(SCENET, SCE_HTTP_ERROR_PARSE_HTTP_NOT_FOUND, "parse http not found"); + } + + return hleLogSuccessI(SCENET, len); +} + +static int sceParseHttpStatusLine(u32 headerAddr, u32 headerLength, u32 httpVersionMajorAddr, u32 httpVersionMinorAddr, u32 httpStatusCodeAddr, u32 httpStatusCommentAddr, u32 httpStatusCommentLengthAddr) { + WARN_LOG(SCENET, "UNTESTED sceParseHttpStatusLine(%x, %d, %x, %x, %x, %x, %x) at %08x", headerAddr, headerLength, httpVersionMajorAddr, httpVersionMinorAddr, httpStatusCodeAddr, httpStatusCommentAddr, httpStatusCommentLengthAddr, currentMIPS->pc); + if (!Memory::IsValidRange(headerAddr, headerLength)) + return hleLogError(SCENET, -1, "invalid arg"); + + std::string headers(Memory::GetCharPointer(headerAddr), headerLength); + // Get first line from header, line ending can be '\n' or '\r' + std::istringstream hdr(headers); + std::string line; + std::getline(hdr, line, '\n'); + hdr.str(line); + std::getline(hdr, line, '\r'); + NotifyMemInfo(MemBlockFlags::READ, headerAddr, (u32)line.size(), "ParseHttpStatusLine"); + DEBUG_LOG(SCENET, "Headers: %s", headers.c_str()); + + // Split the string by pattern, based on JPCSP + std::regex rgx("HTTP/(\\d)\\.(\\d)\\s+(\\d+)(.*)"); + std::smatch matches; + + if (!std::regex_search(line, matches, rgx)) + return hleLogError(SCENET, -1, "invalid arg"); // SCE_HTTP_ERROR_PARSE_HTTP_NOT_FOUND + + try { + // Convert and Store the value + if (Memory::IsValidRange(httpVersionMajorAddr, 4)) { + Memory::WriteUnchecked_U32(stoi(matches[1].str()), httpVersionMajorAddr); + NotifyMemInfo(MemBlockFlags::WRITE, httpVersionMajorAddr, 4, "ParseHttpStatusLine"); + } + + if (Memory::IsValidRange(httpVersionMinorAddr, 4)) { + Memory::WriteUnchecked_U32(stoi(matches[2].str()), httpVersionMinorAddr); + NotifyMemInfo(MemBlockFlags::WRITE, httpVersionMinorAddr, 4, "ParseHttpStatusLine"); + } + + if (Memory::IsValidRange(httpStatusCodeAddr, 4)) { + Memory::WriteUnchecked_U32(stoi(matches[3].str()), httpStatusCodeAddr); + NotifyMemInfo(MemBlockFlags::WRITE, httpStatusCodeAddr, 4, "ParseHttpStatusLine"); + } + + std::string sc = matches[4].str(); + if (Memory::IsValidRange(httpStatusCommentAddr, 4)) { + Memory::WriteUnchecked_U32(headerAddr + (int)headers.find(sc), httpStatusCommentAddr); + NotifyMemInfo(MemBlockFlags::WRITE, httpStatusCommentAddr, 4, "ParseHttpStatusLine"); + } + + if (Memory::IsValidRange(httpStatusCommentLengthAddr, 4)) { + Memory::WriteUnchecked_U32((u32)sc.size(), httpStatusCommentLengthAddr); + NotifyMemInfo(MemBlockFlags::WRITE, httpStatusCommentLengthAddr, 4, "ParseHttpStatusLine"); + } + } + catch (const std::runtime_error& ex) { + hleLogError(SCENET, -1, "Runtime error: %s", ex.what()); + } + catch (const std::exception& ex) { + hleLogError(SCENET, -1, "Error occurred: %s", ex.what()); // SCE_HTTP_ERROR_PARSE_HTTP_INVALID_VALUE + } + catch (...) { + hleLogError(SCENET, -1, "Unknown error"); + } + + return 0; +} const HLEFunction sceParseHttp [] = { - {0X8077A433, nullptr, "sceParseHttpStatusLine", '?', ""}, - {0XAD7BFDEF, nullptr, "sceParseHttpResponseHeader", '?', ""}, + {0X8077A433, &WrapI_UUUUUUU, "sceParseHttpStatusLine", 'i', "xxxxxxx"}, + {0XAD7BFDEF, &WrapI_UICUU, "sceParseHttpResponseHeader", 'i', "xisxx" }, }; void Register_sceParseHttp() { RegisterModule("sceParseHttp", ARRAY_SIZE(sceParseHttp), sceParseHttp); -} \ No newline at end of file +} diff --git a/Core/HLE/sceParseUri.cpp b/Core/HLE/sceParseUri.cpp index b8b5e15cd6e7..86e8138fe0d1 100644 --- a/Core/HLE/sceParseUri.cpp +++ b/Core/HLE/sceParseUri.cpp @@ -17,16 +17,151 @@ #include "Core/HLE/HLE.h" #include "Core/HLE/sceParseUri.h" +#include "Core/HLE/FunctionWrappers.h" +#include "Core/Debugger/MemBlockInfo.h" +#include "Common/Net/URL.h" +#include "Common/StringUtils.h" + + +int workAreaAddString(u32 workAreaAddr, int workAreaSize, int offset, const char* s) { + const std::string str = (s? s:""); + + int length = (int)str.length() + 1; // added space for null termination + if (offset + length > workAreaSize) { + length = workAreaSize - offset; + if (length <= 0) { + return offset; + } + } + + Memory::MemcpyUnchecked(workAreaAddr + offset, str.c_str(), length); + return offset + length; +} + +// FIXME: parsedUriArea, workArea can be 0/null? LittelBigPlanet seems to set workAreaSizeAddr to 0 +static int sceUriParse(u32 parsedUriAreaAddr, const char* url, u32 workAreaAddr, u32 workAreaSizeAddr, int workAreaSize) { + WARN_LOG(SCENET, "UNTESTED sceUriParse(%x, %s, %x, %x, %d) at %08x", parsedUriAreaAddr, safe_string(url), workAreaAddr, workAreaSizeAddr, workAreaSize, currentMIPS->pc); + if (url == nullptr) + return hleLogError(SCENET, -1, "invalid arg"); + + auto workAreaSz = PSPPointer::Create(workAreaSizeAddr); + + // Size returner + if (parsedUriAreaAddr == 0 || workAreaAddr == 0) { + // Based on JPCSP: The required workArea size is maximum the size of the URL + 7 times the null-byte for string termination. + int sz = (int)strlen(url) + 7; + if (workAreaSz.IsValid()) { + *workAreaSz = sz; + workAreaSz.NotifyWrite("UriParse"); + } + return hleLogSuccessI(SCENET, 0, "workAreaSize: %d, %d", sz, workAreaSize); + } + + auto parsedUri = PSPPointer::Create(parsedUriAreaAddr); + + // Parse the URL into URI components + // Full URI = scheme ":" ["//" [username [":" password] "@"] host [":" port]] path ["?" query] ["#" fragment] + Url uri(url); + // FIXME: URI without "://" should be valid too, due to PSPParsedUri.noSlash property + if (!uri.Valid()) { + return hleLogError(SCENET, -1, "invalid arg"); + } + + // Parse Host() into userInfo (in the format ":") and host + std::string host = uri.Host(); + std::string userInfoUserName = ""; + std::string userInfoPassword = ""; + // Extract Host + size_t pos = host.find('@'); + if (pos <= host.size()) { + userInfoUserName = host.substr(0, pos); + host.erase(0, pos + 1LL); // removing the "@" + } + // Extract UserName and Password + pos = userInfoUserName.find(':'); + if (pos <= userInfoUserName.size()) { + userInfoPassword = userInfoUserName.substr(pos + 1LL); // removing the ":" + userInfoUserName.erase(pos); + } + + // Parse Resource() into path(/), query(?), and fragment(#) + std::string path = uri.Resource(); + std::string query = ""; + std::string fragment = ""; + // Extract Path + pos = path.find('?'); + if (pos <= path.size()) { + query = path.substr(pos); // Not removing the leading "?". Query must have the leading "?" if it's not empty (according to JPCSP) + if (query.size() == 1) + query.clear(); + path.erase(pos); + } + // Extract Query and Fragment + pos = query.find('#'); + if (pos <= query.size()) { + fragment = query.substr(pos); // FIXME: Should we remove the leading "#" ? probably not, just like query + query.erase(pos); + } + + // FIXME: Related to "scheme-specific-part" (ie. parts after the scheme + ":") ? 0 = start with "//" + pos = std::string(url).find("://"); + if (parsedUri.IsValid()) + parsedUri->noSlash = (pos != std::string::npos) ? 0 : 1; + + // Store the URI components in sequence into workAreanand store the respective addresses into the parsedUri structure. + int offset = 0; + if (parsedUri.IsValid()) + parsedUri->schemeAddr = workAreaAddr + offset; + offset = workAreaAddString(workAreaAddr, workAreaSize, offset, uri.Protocol().c_str()); // FiXME: scheme in lowercase, while protocol in uppercase? + + if (parsedUri.IsValid()) + parsedUri->userInfoUserNameAddr = workAreaAddr + offset; + offset = workAreaAddString(workAreaAddr, workAreaSize, offset, userInfoUserName.c_str()); + + if (parsedUri.IsValid()) + parsedUri->userInfoPasswordAddr = workAreaAddr + offset; + offset = workAreaAddString(workAreaAddr, workAreaSize, offset, userInfoPassword.c_str()); + + if (parsedUri.IsValid()) + parsedUri->hostAddr = workAreaAddr + offset; + offset = workAreaAddString(workAreaAddr, workAreaSize, offset, host.c_str()); + + if (parsedUri.IsValid()) + parsedUri->pathAddr = workAreaAddr + offset; + offset = workAreaAddString(workAreaAddr, workAreaSize, offset, path.c_str()); + + if (parsedUri.IsValid()) + parsedUri->queryAddr = workAreaAddr + offset; + offset = workAreaAddString(workAreaAddr, workAreaSize, offset, query.c_str()); + + if (parsedUri.IsValid()) + parsedUri->fragmentAddr = workAreaAddr + offset; + offset = workAreaAddString(workAreaAddr, workAreaSize, offset, fragment.c_str()); + + if (parsedUri.IsValid()) { + parsedUri->port = uri.Port(); + memset(parsedUri->unknown, 0, sizeof(parsedUri->unknown)); + parsedUri.NotifyWrite("UriParse"); + } + + // Update the size + if (workAreaSz.IsValid()) { + *workAreaSz = offset; + workAreaSz.NotifyWrite("UriParse"); + } + + return 0; +} const HLEFunction sceParseUri[] = { {0X49E950EC, nullptr, "sceUriEscape", '?', ""}, {0X062BB07E, nullptr, "sceUriUnescape", '?', ""}, - {0X568518C9, nullptr, "sceUriParse", '?', ""}, + {0X568518C9, &WrapI_UCUUI, "sceUriParse", 'i', "xsxxi" }, {0X7EE318AF, nullptr, "sceUriBuild", '?', ""}, }; void Register_sceParseUri() { RegisterModule("sceParseUri", ARRAY_SIZE(sceParseUri), sceParseUri); -} \ No newline at end of file +} diff --git a/Core/HLE/sceParseUri.h b/Core/HLE/sceParseUri.h index 5108cd021986..9b3bb7d06b6a 100644 --- a/Core/HLE/sceParseUri.h +++ b/Core/HLE/sceParseUri.h @@ -17,4 +17,28 @@ #pragma once -void Register_sceParseUri(); \ No newline at end of file +#ifdef _MSC_VER +#define PACK // on MSVC we use #pragma pack() instead so let's kill this. +#pragma pack(push, 1) +#else +#define PACK __attribute__((packed)) +#endif + +typedef struct PSPParsedUri { + s32 noSlash; + s32 schemeAddr; + s32 userInfoUserNameAddr; + s32 userInfoPasswordAddr; + s32 hostAddr; + s32 pathAddr; + s32 queryAddr; + s32 fragmentAddr; + u16 port; + u8 unknown[10]; // padding might be included here? +} PACK PSPParsedUri; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +void Register_sceParseUri(); diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index 0672de41bef1..8370f28fc4b9 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -41,6 +41,7 @@ #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/scePower.h" #include "Core/HLE/sceUtility.h" +#include #include "Core/Dialog/PSPSaveDialog.h" #include "Core/Dialog/PSPMsgDialog.h" @@ -66,6 +67,8 @@ #define PSP_USB_MODULE_CAM 4 // Requires PSP_USB_MODULE_ACC loading first #define PSP_USB_MODULE_GPS 5 // Requires PSP_USB_MODULE_ACC loading first +const int SCE_ERROR_NETPARAM_BAD_NETCONF = 0x80110601; +const int SCE_ERROR_NETPARAM_BAD_PARAM = 0x80110604; const int SCE_ERROR_MODULE_BAD_ID = 0x80111101; const int SCE_ERROR_MODULE_ALREADY_LOADED = 0x80111102; const int SCE_ERROR_MODULE_NOT_LOADED = 0x80111103; @@ -148,6 +151,7 @@ static HLEHelperThread *accessThread = nullptr; static bool accessThreadFinished = true; static const char *accessThreadState = "initial"; static int lastSaveStateVersion = -1; +static int netParamLatestId = 1; static void CleanupDialogThreads(bool force = false) { if (accessThread) { @@ -705,14 +709,252 @@ static int sceUtilityNetconfGetStatus() { return hleLogSuccessVerboseI(SCEUTILITY, status); } +/** +* Check existence of a Net Configuration +* +* @param id - id of net Configuration (1 to n) +* @return 0 on success +* +* Note: some homebrew may only support a limited number of entries (ie. 10 entries) +*/ static int sceUtilityCheckNetParam(int id) { bool available = (id >= 0 && id <= 24); - int ret = available ? 0 : 0X80110601; + // We only have 1 faked net config entry + if (id > PSP_NETPARAM_MAX_NUMBER_DUMMY_ENTRIES) { + available = false; + } + int ret = available ? 0 : SCE_ERROR_NETPARAM_BAD_NETCONF; DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityCheckNetParam(%d)", ret, id); return ret; } +/** +* Get Net Configuration Parameter +* +* @param conf - Net Configuration number (1 to n) (0 returns valid but seems to be a copy of the last config requested) +* @param param - which parameter to get +* @param data - parameter data +* @return 0 on success +*/ +static int sceUtilityGetNetParam(int id, int param, u32 dataAddr) { + DEBUG_LOG(SCEUTILITY, "sceUtilityGetNetParam(%d, %d, %08x)", id, param, dataAddr); + if (id < 0 || id > 24) { + return hleLogWarning(SCEUTILITY, SCE_ERROR_NETPARAM_BAD_NETCONF, "invalid id=%d", id); + } + + // TODO: Replace the temporary netApctlInfo with netConfInfo, since some of netApctlInfo contents supposed to be taken from netConfInfo during ApctlInit, while sceUtilityGetNetParam can be used before Apctl Initialized + char name[APCTL_PROFILENAME_MAXLEN]; + truncate_cpy(name, sizeof(name), (defaultNetConfigName + std::to_string(id==0? netParamLatestId:id)).c_str()); + char dummyWEPKey[6] = "XXXXX"; // WEP 64-bit = 10 hex digits key or 5-digit ASCII equivalent + char dummyUserPass[256] = "PPSSPP"; // FIXME: Username / Password max length = 255 chars? + char dummyWPAKey[64] = "XXXXXXXX"; // FIXME: WPA 256-bit = 64 hex digits key or 8 to 63-chars ASCII passphrases? + switch (param) { + case PSP_NETPARAM_NAME: + if (!Memory::IsValidRange(dataAddr, APCTL_PROFILENAME_MAXLEN)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, name, APCTL_PROFILENAME_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, APCTL_PROFILENAME_MAXLEN, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_SSID: + if (!Memory::IsValidRange(dataAddr, APCTL_SSID_MAXLEN)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, netApctlInfo.ssid, APCTL_SSID_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, APCTL_SSID_MAXLEN, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_SECURE: + // 0 is no security. + // 1 is WEP (64-bit). + // 2 is WEP (128-bit). + // 3 is WPA (256-bit ?). + if (!Memory::IsValidRange(dataAddr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::WriteUnchecked_U32(1, dataAddr); // WEP 64-bit + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 4, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_WEPKEY: + // WEP 64-bit = 10 hex digits key or 5-digit ASCII equivalent + // WEP 128-bit = 26 hex digits key or 13-digit ASCII equivalent + // WEP 256-bit = 58 hex digits key or 29-digit ASCII equivalent + // WPA 256-bit = 64 hex digits key or 8 to 63-chars ASCII passphrases? + if (!Memory::IsValidRange(dataAddr, 5)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, dummyWEPKey, 5); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 5, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_IS_STATIC_IP: + // 0 is DHCP. + // 1 is static. + // 2 is PPPOE. + if (!Memory::IsValidRange(dataAddr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::WriteUnchecked_U32(1, dataAddr); // static IP + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 4, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_IP: + if (!Memory::IsValidRange(dataAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, netApctlInfo.ip, APCTL_IPADDR_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, APCTL_IPADDR_MAXLEN, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_NETMASK: + if (!Memory::IsValidRange(dataAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, netApctlInfo.subNetMask, APCTL_IPADDR_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, APCTL_IPADDR_MAXLEN, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_ROUTE: + if (!Memory::IsValidRange(dataAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, netApctlInfo.gateway, APCTL_IPADDR_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, APCTL_IPADDR_MAXLEN, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_MANUAL_DNS: + // 0 is auto. + // 1 is manual. + if (!Memory::IsValidRange(dataAddr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::WriteUnchecked_U32(1, dataAddr); // manual + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 4, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_PRIMARYDNS: + if (!Memory::IsValidRange(dataAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, netApctlInfo.primaryDns, APCTL_IPADDR_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, APCTL_IPADDR_MAXLEN, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_SECONDARYDNS: + if (!Memory::IsValidRange(dataAddr, APCTL_IPADDR_MAXLEN)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, netApctlInfo.secondaryDns, APCTL_IPADDR_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, APCTL_IPADDR_MAXLEN, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_PROXY_USER: + // FIXME: Proxy's Username max length = 255 chars? + if (!Memory::IsValidRange(dataAddr, 255)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, dummyUserPass, 255); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 255, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_PROXY_PASS: + // FIXME: Proxy's Password max length = 255 chars? + if (!Memory::IsValidRange(dataAddr, 255)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, dummyUserPass, 255); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 255, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_USE_PROXY: + // 0 is to not use proxy. + // 1 is to use proxy. + if (!Memory::IsValidRange(dataAddr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.useProxy, dataAddr); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 4, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_PROXY_SERVER: + if (!Memory::IsValidRange(dataAddr, APCTL_URL_MAXLEN)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, netApctlInfo.proxyUrl, APCTL_URL_MAXLEN); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, APCTL_URL_MAXLEN, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_PROXY_PORT: + if (!Memory::IsValidRange(dataAddr, 2)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::WriteUnchecked_U16(netApctlInfo.proxyPort, dataAddr); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 2, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_VERSION: + // 0 is not used. + // 1 is old version. + // 2 is new version. + if (!Memory::IsValidRange(dataAddr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::WriteUnchecked_U32(2, dataAddr); // new version + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 4, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_UNKNOWN: + if (!Memory::IsValidRange(dataAddr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::WriteUnchecked_U32(0, dataAddr); // reserved? + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 4, "UtilityGetNetParam"); + DEBUG_LOG(SCEUTILITY, "sceUtilityGetNetParam - Unknown Param(%d)", param); + break; + case PSP_NETPARAM_8021X_AUTH_TYPE: + // 0 is none. + // 1 is EAP (MD5). + if (!Memory::IsValidRange(dataAddr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.eapType, dataAddr); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 4, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_8021X_USER: + // FIXME: 8021X's Username max length = 255 chars? + if (!Memory::IsValidRange(dataAddr, 255)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, dummyUserPass, 255); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 255, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_8021X_PASS: + // FIXME: 8021X's Password max length = 255 chars? + if (!Memory::IsValidRange(dataAddr, 255)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, dummyUserPass, 255); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 255, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_WPA_TYPE: + // 0 is key in hexadecimal format. + // 1 is key in ASCII format. + if (!Memory::IsValidRange(dataAddr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::WriteUnchecked_U32(1, dataAddr); // ASCII format + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 4, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_WPA_KEY: + // FIXME: WPA 256-bit = 64 hex digits key or 8 to 63-chars ASCII passphrases? + if (!Memory::IsValidRange(dataAddr, 63)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::MemcpyUnchecked(dataAddr, dummyWPAKey, 63); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 63, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_BROWSER: + // 0 is to not start the native browser. + // 1 is to start the native browser. + if (!Memory::IsValidRange(dataAddr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::WriteUnchecked_U32(netApctlInfo.startBrowser, dataAddr); + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 4, "UtilityGetNetParam"); + break; + case PSP_NETPARAM_WIFI_CONFIG: + // 0 is no config. + // 1 is unknown. (WISP ?) + // 2 is Playstation Spot. + // 3 is unknown. + if (!Memory::IsValidRange(dataAddr, 4)) + return hleLogError(SCENET, -1, "invalid arg"); + Memory::WriteUnchecked_U32(0, dataAddr); // no config / netApctlInfo.wifisp ? + NotifyMemInfo(MemBlockFlags::WRITE, dataAddr, 4, "UtilityGetNetParam"); + break; + default: + return hleLogWarning(SCEUTILITY, SCE_ERROR_NETPARAM_BAD_PARAM, "invalid param=%d", param); + } + + return 0; +} + +/** +* Get Current Net Configuration ID +* +* @param idAddr - Address to store the current net ID (ie. The actual Net Config ID when using ID=0 on sceUtilityGetNetParam ?) +* @return 0 on success +*/ +static int sceUtilityGetNetParamLatestID(u32 idAddr) { + DEBUG_LOG(SCEUTILITY, "sceUtilityGetNetParamLatestID(%08x)", idAddr); + // This function is saving the last net param ID (non-zero ID?) and not the number of net configurations. + Memory::Write_U32(netParamLatestId, idAddr); + + return 0; +} + //TODO: Implement all sceUtilityScreenshot* for real, it doesn't seem to be complex //but it requires more investigation @@ -1078,8 +1320,8 @@ const HLEFunction sceUtility[] = {0X91E70E35, &WrapI_I, "sceUtilityNetconfUpdate", 'i', "i" }, {0X6332AA39, &WrapI_V, "sceUtilityNetconfGetStatus", 'i', "" }, {0X5EEE6548, &WrapI_I, "sceUtilityCheckNetParam", 'i', "i" }, - {0X434D4B3A, nullptr, "sceUtilityGetNetParam", '?', "" }, - {0X4FED24D8, nullptr, "sceUtilityGetNetParamLatestID", '?', "" }, + {0X434D4B3A, &WrapI_IIU, "sceUtilityGetNetParam", 'i', "iix"}, + {0X4FED24D8, &WrapI_U, "sceUtilityGetNetParamLatestID", 'i', "x" }, {0X67AF3428, &WrapI_V, "sceUtilityMsgDialogShutdownStart", 'i', "" }, {0X2AD8E239, &WrapI_U, "sceUtilityMsgDialogInitStart", 'i', "x" }, diff --git a/Core/HLE/sceUtility.h b/Core/HLE/sceUtility.h index 9943fc839936..a654d5092632 100644 --- a/Core/HLE/sceUtility.h +++ b/Core/HLE/sceUtility.h @@ -76,6 +76,36 @@ class PointerWrap; #define PSP_SYSTEMPARAM_BUTTON_CIRCLE 0 #define PSP_SYSTEMPARAM_BUTTON_CROSS 1 +// Valid values for NetParam +#define PSP_NETPARAM_NAME 0 // string +#define PSP_NETPARAM_SSID 1 // string +#define PSP_NETPARAM_SECURE 2 // int +#define PSP_NETPARAM_WEPKEY 3 // string +#define PSP_NETPARAM_IS_STATIC_IP 4 // int +#define PSP_NETPARAM_IP 5 // string +#define PSP_NETPARAM_NETMASK 6 // string +#define PSP_NETPARAM_ROUTE 7 // string +#define PSP_NETPARAM_MANUAL_DNS 8 // int +#define PSP_NETPARAM_PRIMARYDNS 9 // string +#define PSP_NETPARAM_SECONDARYDNS 10 // string +#define PSP_NETPARAM_PROXY_USER 11 // string +#define PSP_NETPARAM_PROXY_PASS 12 // string +#define PSP_NETPARAM_USE_PROXY 13 // int +#define PSP_NETPARAM_PROXY_SERVER 14 // string +#define PSP_NETPARAM_PROXY_PORT 15 // int +#define PSP_NETPARAM_VERSION 16 // int +#define PSP_NETPARAM_UNKNOWN 17 // int +#define PSP_NETPARAM_8021X_AUTH_TYPE 18 // int +#define PSP_NETPARAM_8021X_USER 19 // string +#define PSP_NETPARAM_8021X_PASS 20 // string +#define PSP_NETPARAM_WPA_TYPE 21 // int +#define PSP_NETPARAM_WPA_KEY 22 // string +#define PSP_NETPARAM_BROWSER 23 // int +#define PSP_NETPARAM_WIFI_CONFIG 24 // int + +// X-Men Legends 2, and some homebrew may support up to 10 net config entries, but we currently only have 1 faked net config +#define PSP_NETPARAM_MAX_NUMBER_DUMMY_ENTRIES 1 + enum class UtilityDialogType { NONE, SAVEDATA,