diff --git a/Common/TimeUtil.cpp b/Common/TimeUtil.cpp index 2cb375c9a0..8f0ce379ed 100644 --- a/Common/TimeUtil.cpp +++ b/Common/TimeUtil.cpp @@ -42,18 +42,14 @@ double time_now_d() { #else -static uint64_t _frequency = 0; -static uint64_t _starttime = 0; - double time_now_d() { static time_t start; struct timeval tv; - gettimeofday(&tv, NULL); + gettimeofday(&tv, nullptr); if (start == 0) { start = tv.tv_sec; } - tv.tv_sec -= start; - return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; + return (double)(tv.tv_sec - start) + (double)tv.tv_usec * (1.0 / 1000000.0); } #endif diff --git a/Core/Config.cpp b/Core/Config.cpp index ff21343c4e..f4af6dc1c7 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -988,8 +988,9 @@ static ConfigSetting networkSettings[] = { ConfigSetting("PortOffset", &g_Config.iPortOffset, 0, true, true), ConfigSetting("MinTimeout", &g_Config.iMinTimeout, 1, true, true), ConfigSetting("TCPNoDelay", &g_Config.bTCPNoDelay, false, true, true), + ConfigSetting("ForcedFirstConnect", &g_Config.bForcedFirstConnect, false, true, true), ConfigSetting("EnableUPnP", &g_Config.bEnableUPnP, false, true, true), - ConfigSetting("UPnPUseOriginalPort", &g_Config.bUPnPUseOriginalPort, true, true, true), + ConfigSetting("UPnPUseOriginalPort", &g_Config.bUPnPUseOriginalPort, false, true, true), ConfigSetting("EnableNetworkChat", &g_Config.bEnableNetworkChat, false, true, true), ConfigSetting("ChatButtonPosition",&g_Config.iChatButtonPosition,BOTTOM_LEFT,true,true), @@ -1048,6 +1049,7 @@ static ConfigSetting systemParamSettings[] = { ConfigSetting("WlanPowerSave", &g_Config.bWlanPowerSave, (bool) PSP_SYSTEMPARAM_WLAN_POWERSAVE_OFF, true, true), ReportedConfigSetting("EncryptSave", &g_Config.bEncryptSave, true, true, true), ConfigSetting("SavedataUpgradeVersion", &g_Config.bSavedataUpgrade, true, true, false), + ConfigSetting("MemStickSize", &g_Config.iMemStickSizeGB, 16, true, false), ConfigSetting(false), }; diff --git a/Core/Config.h b/Core/Config.h index cee5c7ed75..a3828cd7da 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -130,6 +130,7 @@ public: std::string sRemoteISOSubdir; bool bRemoteDebuggerOnStartup; bool bMemStickInserted; + int iMemStickSizeGB; bool bLoadPlugins; int iScreenRotation; // The rotation angle of the PPSSPP UI. Only supported on Android and possibly other mobile platforms. @@ -433,6 +434,7 @@ public: bool bTCPNoDelay; bool bEnableUPnP; bool bUPnPUseOriginalPort; + bool bForcedFirstConnect; int iPortOffset; int iMinTimeout; int iWlanAdhocChannel; diff --git a/Core/Dialog/PSPNetconfDialog.cpp b/Core/Dialog/PSPNetconfDialog.cpp index 3dfb8d45c7..1de5deb3d0 100644 --- a/Core/Dialog/PSPNetconfDialog.cpp +++ b/Core/Dialog/PSPNetconfDialog.cpp @@ -44,9 +44,8 @@ static const float FONT_SCALE = 0.65f; // Needs testing. -const static int NET_INIT_DELAY_US = 300000; -const static int NET_SHUTDOWN_DELAY_US = 260000; -const static int NET_RUNNING_DELAY_US = 1000000; // KHBBS is showing adhoc dialog for about 3-4 seconds, but feels too long, so we're faking it to 1 sec instead to let players read the text +const static int NET_INIT_DELAY_US = 200000; +const static int NET_SHUTDOWN_DELAY_US = 200000; const static int NET_CONNECT_TIMEOUT = 15000000; // Using 15 secs to match the timeout on Adhoc Server side (SERVER_USER_TIMEOUT) struct ScanInfos { @@ -306,27 +305,24 @@ int PSPNetconfDialog::Update(int animSpeed) { } 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) { if (pendingStatus != SCE_UTILITY_STATUS_FINISHED) { - ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_RUNNING_DELAY_US); - } - else if (GetStatus() == SCE_UTILITY_STATUS_FINISHED) { - // We are done! StartFade(false); + ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US); } } - else if (state == PSP_NET_APCTL_STATE_GETTING_IP) { + 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. - connResult = sceNetApctlConnect(1); + // When connecting with infrastructure, simulate a connection using the first network configuration entry. + if (connResult < 0) { + connResult = sceNetApctlConnect(1); + } } } @@ -442,17 +438,14 @@ int PSPNetconfDialog::Update(int animSpeed) { } } - // The Netconf dialog stays visible until the network reaches - // the state ADHOCCTL_STATE_CONNECTED. + // The Netconf dialog stays visible until the network reaches the state ADHOCCTL_STATE_CONNECTED. if (state == ADHOCCTL_STATE_CONNECTED) { // Checking pendingStatus to make sure ChangeStatus not to continously extending the delay ticks on every call for eternity if (pendingStatus != SCE_UTILITY_STATUS_FINISHED) { - ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_RUNNING_DELAY_US); - } - // Start fading only when the actual status has changed - else if (GetStatus() == SCE_UTILITY_STATUS_FINISHED) { StartFade(false); + ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US); } + // Let's not leaks any memory if (Memory::IsValidAddress(scanInfosAddr)) userMemory.Free(scanInfosAddr); diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 269eb3a33f..5c3fc8e2c4 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -1417,7 +1417,7 @@ int SavedataParam::SetPspParam(SceUtilitySavedataParam *param) saveDataListCount = (int)allSaves.size(); saveDataList = new SaveFileInfo[saveDataListCount]; for (auto save : allSaves) { - if (save.name == "." || save.name == "..") + if (save.type != FILETYPE_DIRECTORY || save.name == "." || save.name == "..") continue; std::string fileDataDir = savePath + save.name; PSPFileInfo info = pspFileSystem.GetFileInfo(fileDataDir); diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp index 31f7bf5689..c7f3b46c1c 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -669,3 +669,17 @@ void MetaFileSystem::DoState(PointerWrap &p) } } +u64 MetaFileSystem::getDirSize(const std::string &dirPath) { + u64 result = 0; + auto allFiles = GetDirListing(dirPath); + for (auto file : allFiles) { + if (file.name == "." || file.name == "..") + continue; + if (file.type == FILETYPE_DIRECTORY) { + result += getDirSize(dirPath + file.name + "/"); + } else { + result += file.size; + } + } + return result; +} diff --git a/Core/FileSystems/MetaFileSystem.h b/Core/FileSystems/MetaFileSystem.h index 8294ba16da..5d3984349a 100644 --- a/Core/FileSystems/MetaFileSystem.h +++ b/Core/FileSystems/MetaFileSystem.h @@ -129,4 +129,6 @@ public: std::lock_guard guard(lock); startingDirectory = dir; } + + u64 getDirSize(const std::string &dirPath); }; diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 425bb63413..34fafd6f4a 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -123,12 +123,15 @@ bool isPDPPortInUse(uint16_t port) { return false; } -bool isPTPPortInUse(uint16_t port) { +bool isPTPPortInUse(uint16_t port, bool forListen) { // Iterate Sockets for (int i = 0; i < MAX_SOCKET; i++) { auto sock = adhocSockets[i]; if (sock != NULL && sock->type == SOCK_PTP) - if (sock->data.ptp.lport == port) + // It's allowed to Listen and Open the same PTP port, But it's not allowed to Listen or Open the same PTP port twice. + if (sock->data.ptp.lport == port && + ((forListen && sock->data.ptp.state == ADHOC_PTP_STATE_LISTEN) || + (!forListen && sock->data.ptp.state != ADHOC_PTP_STATE_LISTEN))) return true; } // Unused Port @@ -1412,7 +1415,7 @@ int friendFinder(){ getLocalMac(&localMac); if (std::find_if(gameModeMacs.begin(), gameModeMacs.end(), [localMac](SceNetEtherAddr const& e) { - return IsMatch(e, localMac); + return isMacMatch(&e, &localMac); }) == gameModeMacs.end()) { // Arrange the order to be consistent on all players (Host on top), Starting from our self the rest of new players will be added to the back gameModeMacs.push_back(localMac); @@ -1501,14 +1504,14 @@ int friendFinder(){ if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) { if (std::find_if(gameModeMacs.begin(), gameModeMacs.end(), [packet](SceNetEtherAddr const& e) { - return IsMatch(e, packet->mac); + return isMacMatch(&e, &packet->mac); }) == gameModeMacs.end()) { // Arrange the order to be consistent on all players (Host on top), Existing players are sent in reverse by AdhocServer SceNetEtherAddr localMac; getLocalMac(&localMac); auto it = std::find_if(gameModeMacs.begin(), gameModeMacs.end(), [localMac](SceNetEtherAddr const& e) { - return IsMatch(e, localMac); + return isMacMatch(&e, &localMac); }); // Starting from our self the rest of new players will be added to the back if (it != gameModeMacs.end()) { @@ -1572,8 +1575,8 @@ int friendFinder(){ /*if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) { auto peer = findFriendByIP(packet->ip); gameModeMacs.erase(std::remove_if(gameModeMacs.begin(), gameModeMacs.end(), - [peer](auto const& e) { - return IsMatch(e, peer->mac_addr); + [peer](SceNetEtherAddr const& e) { + return isMacMatch(&e, &peer->mac_addr); }), gameModeMacs.end()); }*/ @@ -1985,7 +1988,7 @@ int getNicknameCount(const char * nickname) for (; peer != NULL; peer = peer->next) { // Match found - if (strncmp((char *)&peer->nickname.data, nickname, ADHOCCTL_NICKNAME_LEN) == 0) count++; + if (peer->last_recv != 0 && strncmp((char *)&peer->nickname.data, nickname, ADHOCCTL_NICKNAME_LEN) == 0) count++; } // Return Result @@ -2078,9 +2081,9 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ errorcode = errno; if (iResult == SOCKET_ERROR && errorcode != EISCONN) { - u64 startTime = (u64)(time_now_d() * 1000.0); + u64 startTime = (u64)(time_now_d() * 1000000.0); while (IsSocketReady(metasocket, false, true) <= 0) { - u64 now = (u64)(time_now_d() * 1000.0); + u64 now = (u64)(time_now_d() * 1000000.0); if (coreState == CORE_POWERDOWN) return iResult; if (now - startTime > adhocDefaultTimeout) break; sleep_ms(10); @@ -2102,7 +2105,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ packet.name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; memcpy(packet.game.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN); - IsSocketReady(metasocket, false, true, nullptr, adhocDefaultTimeout * 1000); + IsSocketReady(metasocket, false, true, nullptr, adhocDefaultTimeout); int sent = send(metasocket, (char*)&packet, sizeof(packet), MSG_NOSIGNAL); if (sent > 0) { socklen_t addrLen = sizeof(LocalIP); diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index d5997025d5..94874334e0 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -56,6 +56,7 @@ #ifdef _WIN32 #undef errno +#undef ESHUTDOWN #undef ECONNABORTED #undef ECONNRESET #undef ENOTCONN @@ -65,6 +66,7 @@ #undef EALREADY #undef ETIMEDOUT #define errno WSAGetLastError() +#define ESHUTDOWN WSAESHUTDOWN #define ECONNABORTED WSAECONNABORTED #define ECONNRESET WSAECONNRESET #define ENOTCONN WSAENOTCONN @@ -74,11 +76,13 @@ #define EALREADY WSAEALREADY #define ETIMEDOUT WSAETIMEDOUT inline bool connectInProgress(int errcode){ return (errcode == WSAEWOULDBLOCK || errcode == WSAEINPROGRESS || errcode == WSAEALREADY); } +inline bool isDisconnected(int errcode) { return (errcode == WSAECONNRESET || errcode == WSAECONNABORTED || errcode == WSAESHUTDOWN); } #else #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #define closesocket close inline bool connectInProgress(int errcode){ return (errcode == EAGAIN || errcode == EWOULDBLOCK || errcode == EINPROGRESS || errcode == EALREADY); } +inline bool isDisconnected(int errcode) { return (errcode == EPIPE || errcode == ECONNRESET || errcode == ECONNABORTED || errcode == ESHUTDOWN); } #endif #ifndef POLL_ERR @@ -104,11 +108,9 @@ inline bool connectInProgress(int errcode){ return (errcode == EAGAIN || errcode // Server Listening Port #define SERVER_PORT 27312 -// Default GameMode Port +// Default GameMode definitions #define ADHOC_GAMEMODE_PORT 31000 - -#define GAMEMODE_UPDATE_INTERVAL 12000 // usec, based on JPCSP -#define GAMEMODE_BUFFER_SIZE 0x8000 +#define GAMEMODE_UPDATE_INTERVAL 10000 // 12000 usec on JPCSP, but 10000 works better on BattleZone (in order to get full speed 60 FPS) // psp strutcs and definitions #define ADHOCCTL_MODE_NONE -1 @@ -156,15 +158,15 @@ inline bool connectInProgress(int errcode){ return (errcode == EAGAIN || errcode #define ADHOC_EV_CONNECT 0x0004 #define ADHOC_EV_ACCEPT 0x0008 #define ADHOC_EV_FLUSH 0x0010 -#define ADHOC_EV_INVALID 0x0100 -#define ADHOC_EV_DELETE 0x0200 +#define ADHOC_EV_INVALID 0x0100 // ignored on events but can be raised on revents? similar to POLLNVAL on posix poll? +#define ADHOC_EV_DELETE 0x0200 // ignored on events but can be raised on revents? similar to POLLERR on posix poll? #define ADHOC_EV_ALERT 0x0400 -#define ADHOC_EV_DISCONNECT 0x0800 +#define ADHOC_EV_DISCONNECT 0x0800 // ignored on events but can be raised on revents? similar to POLLHUP on posix poll? // PTP Connection States #define ADHOC_PTP_STATE_CLOSED 0 #define ADHOC_PTP_STATE_LISTEN 1 -#define ADHOC_PTP_STATE_SYN_SENT 2 +#define ADHOC_PTP_STATE_SYN_SENT 2 // 3-way handshake normally: [client]send SYN -> [server]recv SYN and reply with ACK+SYN -> [client]recv SYN and reply with ACK -> Established #define ADHOC_PTP_STATE_SYN_RCVD 3 #define ADHOC_PTP_STATE_ESTABLISHED 4 @@ -209,9 +211,9 @@ extern uint8_t broadcastMAC[ETHER_ADDR_LEN]; // Malloc Pool Information typedef struct SceNetMallocStat { - s32_le pool; // Pointer to the pool? // This should be the poolSize isn't? - s32_le maximum; // Maximum size of the pool? Maximum usage (ie. pool- free) ? - s32_le free; // How much memory is free + s32_le pool; // On Vantage Master Portable this is 0x1ffe0 on sceNetGetMallocStat, while the poolSize arg on sceNetInit was 0x20000 + s32_le maximum; // On Vantage Master Portable this is 0x4050, Footprint of Highest amount allocated so far? + s32_le free; // On Vantage Master Portable this is 0x1f300, VMP compares this value with required size before sending data } PACK SceNetMallocStat; // Adhoc Virtual Network Name @@ -303,6 +305,7 @@ typedef struct SceNetAdhocctlGameModeInfo { // GameModeUpdateInfo typedef struct GameModeUpdateInfo { + u32_le length; //size of GameModeUpdateInfo (16 bytes) s32_le updated; u64_le timeStamp; } PACK GameModeUpdateInfo; @@ -353,10 +356,14 @@ typedef struct AdhocSocket { s32_le type; // SOCK_PDP/SOCK_PTP s32_le flags; // Socket Alert Flags s32_le alerted_flags; // Socket Alerted Flags + s32_le nonblocking; // last non-blocking flag + u32 buffer_size; u32 send_timeout; // default connect timeout u32 recv_timeout; // default accept timeout - s32 retry_count; // combined with timeout to be used on keepalive + s32 retry_interval; // related to keepalive + s32 retry_count; // multiply with retry interval to be used as keepalive timeout s32 attemptCount; // connect/accept attempts + u64 lastAttempt; // timestamp to retry again union { SceNetAdhocPdpStat pdp; SceNetAdhocPtpStat ptp; @@ -943,12 +950,14 @@ bool isLocalMAC(const SceNetEtherAddr * addr); bool isPDPPortInUse(uint16_t port); /** - * Check whether PTP Port is in use or not + * Check whether PTP Port is in use or not (only sockets with non-Listening state will be considered as in use) * @param port To-be-checked Port Number + * @param forListen to check for listening or non-listening port * @return 1 if in use or... 0 */ -bool isPTPPortInUse(uint16_t port); +bool isPTPPortInUse(uint16_t port, bool forListen); +// Convert MAC address to string std::string mac2str(SceNetEtherAddr* mac); /* diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index cf733955b6..4eb3f848e5 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -619,6 +619,9 @@ static void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) { skipFrame = true; } + // TODO: This is NOT where we should wait, really! We should mark each outgoing frame with the desired + // timestamp to push it to display, and sleep in the render thread to achieve that. + if (curFrameTime < nextFrameTime && throttle) { // If time gap is huge just jump (somebody unthrottled) if (nextFrameTime - curFrameTime > 2*scaledTimestep) { diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 74d01aa789..2de660cc5e 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -68,6 +68,7 @@ u32 netApctlState; u32 apctlThreadHackAddr = 0; u32_le apctlThreadCode[3]; SceUID apctlThreadID = 0; +int apctlStateEvent = -1; int actionAfterApctlMipsCall; std::recursive_mutex apctlEvtMtx; std::deque apctlEvents; @@ -125,10 +126,47 @@ void InitLocalhostIP() { isLocalServer = (!strcasecmp(serverStr.c_str(), "localhost") || serverStr.find("127.") == 0); } +static void __ApctlState(u64 userdata, int cyclesLate) { + SceUID threadID = userdata >> 32; + int uid = (int)(userdata & 0xFFFFFFFF); + int event = uid - 1; + + s64 result = 0; + u32 error = 0; + + SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error); + if (waitID == 0 || error != 0) + return; + + u32 waitVal = __KernelGetWaitValue(threadID, error); + if (error == 0) { + netApctlState = waitVal; + } + + __KernelResumeThreadFromWait(threadID, result); + DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetApctl - Event: %d, State: %d", waitID, error, (int)result, event, netApctlState); +} + +// Used to change Apctl State after a delay and before executing callback mipscall (since we don't have beforeAction) +int ScheduleApctlState(int event, int newState, int usec, const char* reason) { + int uid = event + 1; + + if (apctlStateEvent < 0) + apctlStateEvent = CoreTiming::RegisterEvent("__ApctlState", __ApctlState); + + u64 param = ((u64)__KernelGetCurThread()) << 32 | uid; + CoreTiming::ScheduleEvent(usToCycles(usec), apctlStateEvent, param); + __KernelWaitCurThread(WAITTYPE_NET, uid, newState, 0, false, reason); + + return 0; +} + void __NetApctlInit() { netApctlInited = false; netApctlState = PSP_NET_APCTL_STATE_DISCONNECTED; + apctlStateEvent = CoreTiming::RegisterEvent("__ApctlState", __ApctlState); apctlHandlers.clear(); + apctlEvents.clear(); memset(&netApctlInfo, 0, sizeof(netApctlInfo)); } @@ -182,6 +220,8 @@ void __NetApctlShutdown() { kernelMemory.Free(apctlThreadHackAddr); apctlThreadHackAddr = 0; } + apctlHandlers.clear(); + apctlEvents.clear(); } void __NetShutdown() { @@ -216,7 +256,7 @@ void netValidateLoopMemory() { // This feels like a dubious proposition, mostly... void __NetDoState(PointerWrap &p) { - auto s = p.Section("sceNet", 1, 4); + auto s = p.Section("sceNet", 1, 5); if (!s) return; @@ -260,6 +300,15 @@ void __NetDoState(PointerWrap &p) { apctlThreadHackAddr = 0; apctlThreadID = 0; } + if (s >= 5) { + Do(p, apctlStateEvent); + if (apctlStateEvent != -1) { + CoreTiming::RestoreRegisterEvent(apctlStateEvent, "__ApctlState", __ApctlState); + } + } + else { + apctlStateEvent = -1; + } 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 @@ -370,78 +419,106 @@ std::string error2str(u32 errorCode) { void __NetApctlCallbacks() { std::lock_guard apctlGuard(apctlEvtMtx); + 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& id = args.data[0]; + auto& result = args.data[1]; + auto& argAddr = args.data[2]; + npAuthEvents.pop_front(); + + delayus = (adhocEventDelay + adhocExtraDelay); + + int handlerID = id - 1; + for (std::map::iterator it = npAuthHandlers.begin(); it != npAuthHandlers.end(); ++it) { + if (it->first == handlerID) { + DEBUG_LOG(SCENET, "NpAuthCallback [HandlerID=%i][RequestID=%d][Result=%d][ArgsPtr=%08x]", it->first, id, result, it->second.argument); + // TODO: Update result / args.data[1] with the actual ticket length (or error code?) + hleEnqueueCall(it->second.entryPoint, 3, 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 oldState = &args.data[0]; - auto newState = &args.data[1]; - auto event = &args.data[2]; - auto error = &args.data[3]; + auto& oldState = args.data[0]; + auto& newState = args.data[1]; + auto& event = args.data[2]; + auto& error = args.data[3]; apctlEvents.pop_front(); - // Adjust delay according to current event. Added an extra delay to prevent I/O Timing method from causing disconnection - if (*event == PSP_NET_APCTL_EVENT_CONNECT_REQUEST || *event == PSP_NET_APCTL_EVENT_GET_IP || *event == PSP_NET_APCTL_EVENT_SCAN_REQUEST) - delayus = (adhocEventDelayMS + 2 * adhocExtraPollDelayMS) * 1000; + // 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) + delayus = adhocEventDelay; else - delayus = (adhocEventPollDelayMS + 2 * adhocExtraPollDelayMS) * 1000; + delayus = adhocEventPollDelay; // Do we need to change the oldState? even if there was error? - //if (*error == 0) - *oldState = netApctlState; + //if (error == 0) + // oldState = netApctlState; // 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 - //*error = 0; - switch (*event) { + // 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? + //error = 0; + switch (event) { case PSP_NET_APCTL_EVENT_CONNECT_REQUEST: - netApctlState = 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({ netApctlState, netApctlState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); // Should we use PSP_NET_APCTL_EVENT_EAP_AUTH if securityType is not NONE? + 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? break; case PSP_NET_APCTL_EVENT_ESTABLISHED: - netApctlState = PSP_NET_APCTL_STATE_GETTING_IP; - if (*error == 0) apctlEvents.push_front({ netApctlState, netApctlState, PSP_NET_APCTL_EVENT_GET_IP, 0 }); + newState = PSP_NET_APCTL_STATE_GETTING_IP; + if (error == 0) + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_GET_IP, 0 }); break; case PSP_NET_APCTL_EVENT_GET_IP: - netApctlState = PSP_NET_APCTL_STATE_GOT_IP; + newState = PSP_NET_APCTL_STATE_GOT_IP; NetApctl_InitInfo(); break; case PSP_NET_APCTL_EVENT_DISCONNECT_REQUEST: - netApctlState = PSP_NET_APCTL_STATE_DISCONNECTED; + newState = PSP_NET_APCTL_STATE_DISCONNECTED; break; case PSP_NET_APCTL_EVENT_SCAN_REQUEST: - netApctlState = PSP_NET_APCTL_STATE_SCANNING; - if (*error == 0) apctlEvents.push_front({ netApctlState, netApctlState, PSP_NET_APCTL_EVENT_SCAN_COMPLETE, 0 }); + newState = PSP_NET_APCTL_STATE_SCANNING; + if (error == 0) + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_SCAN_COMPLETE, 0 }); break; case PSP_NET_APCTL_EVENT_SCAN_COMPLETE: - netApctlState = PSP_NET_APCTL_STATE_DISCONNECTED; + newState = PSP_NET_APCTL_STATE_DISCONNECTED; break; case PSP_NET_APCTL_EVENT_EAP_AUTH: // Is this suppose to happen between JOINING and ESTABLISHED ? - netApctlState = PSP_NET_APCTL_STATE_EAP_AUTH; - if (*error == 0) apctlEvents.push_front({ netApctlState, netApctlState, PSP_NET_APCTL_EVENT_KEY_EXCHANGE, 0 }); // not sure if KEY_EXCHANGE is the next step after AUTH or not tho + 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 break; case PSP_NET_APCTL_EVENT_KEY_EXCHANGE: // Is this suppose to happen between JOINING and ESTABLISHED ? - netApctlState = PSP_NET_APCTL_STATE_KEY_EXCHANGE; - if (*error == 0) apctlEvents.push_front({ netApctlState, netApctlState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); + newState = PSP_NET_APCTL_STATE_KEY_EXCHANGE; + if (error == 0) + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); break; case PSP_NET_APCTL_EVENT_RECONNECT: - netApctlState = PSP_NET_APCTL_STATE_DISCONNECTED; - if (*error == 0) apctlEvents.push_front({ netApctlState, netApctlState, PSP_NET_APCTL_EVENT_CONNECT_REQUEST, 0 }); + newState = PSP_NET_APCTL_STATE_DISCONNECTED; + if (error == 0) + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_CONNECT_REQUEST, 0 }); break; } // Do we need to change the newState? even if there were error? - //if (*error == 0) - *newState = netApctlState; + //if (error != 0) + // newState = netApctlState; // Since 0 is a valid index to types_ we use -1 to detects if it was loaded from an old save state if (actionAfterApctlMipsCall < 0) { @@ -450,38 +527,19 @@ void __NetApctlCallbacks() // Run mipscall. Should we skipped executing the mipscall if oldState == newState? for (std::map::iterator it = apctlHandlers.begin(); it != apctlHandlers.end(); ++it) { - DEBUG_LOG(SCENET, "ApctlCallback [ID=%i][OldState=%d][NewState=%d][Event=%d][Error=%d][ArgsPtr=%08x]", it->first, *oldState, *newState, *event, *error, it->second.argument); + DEBUG_LOG(SCENET, "ApctlCallback [ID=%i][OldState=%d][NewState=%d][Event=%d][Error=%08x][ArgsPtr=%08x]", it->first, oldState, newState, event, error, it->second.argument); args.data[4] = it->second.argument; AfterApctlMipsCall* after = (AfterApctlMipsCall*)__KernelCreateAction(actionAfterApctlMipsCall); - after->SetData(it->first, *oldState, *newState, *event, *error, it->second.argument); + after->SetData(it->first, oldState, newState, event, error, it->second.argument); hleEnqueueCall(it->second.entryPoint, 5, args.data, after); } + // Similar to Adhocctl, new State might need to be set after delayed, right before executing the mipscall (ie. simulated beforeAction) + ScheduleApctlState(event, newState, delayus, "apctl callback state"); + return; } - // We are temporarily borrowing APctl thread for NpAuth callbacks for testing to simulate authentication - if (!npAuthEvents.empty()) - { - auto args = npAuthEvents.front(); - auto id = &args.data[0]; - auto result = &args.data[1]; - auto argAddr = &args.data[2]; - npAuthEvents.pop_front(); - - delayus = (adhocEventDelayMS + 2 * adhocExtraPollDelayMS) * 1000; - - int handlerID = *id - 1; - for (std::map::iterator it = npAuthHandlers.begin(); it != npAuthHandlers.end(); ++it) { - if (it->first == handlerID) { - DEBUG_LOG(SCENET, "NpAuthCallback [HandlerID=%i][RequestID=%d][Result=%d][ArgsPtr=%08x]", it->first, *id, *result, it->second.argument); - // TODO: Update result / args.data[1] with the actual ticket length (or error code?) - hleEnqueueCall(it->second.entryPoint, 3, args.data); - } - } - } - - // Must be delayed long enough whenever there is a pending callback. + // Must be delayed long enough whenever there is a pending callback to make sure previous callback & it's afterAction are fully executed sceKernelDelayThread(delayus); - hleSkipDeadbeef(); } static inline u32 AllocUser(u32 size, bool fromTop, const char *name) { @@ -535,7 +593,7 @@ static u32 sceNetTerm() { int retval = Net_Term(); // Give time to make sure everything are cleaned up - hleDelayResult(retval, "give time to init/cleanup", adhocEventDelayMS * 1000); + hleEatMicro(adhocDefaultDelay); return retval; } @@ -583,14 +641,15 @@ static int sceNetInit(u32 poolSize, u32 calloutPri, u32 calloutStack, u32 netini } WARN_LOG(SCENET, "sceNetInit(poolsize=%d, calloutpri=%i, calloutstack=%d, netintrpri=%i, netintrstack=%d) at %08x", poolSize, calloutPri, calloutStack, netinitPri, netinitStack, currentMIPS->pc); - netInited = true; - netMallocStat.pool = poolSize; // This should be the poolSize isn't? - netMallocStat.maximum = poolSize/2; // According to JPCSP's sceNetGetMallocStat this is Currently Used size = (poolSize - free), faked to half the pool - netMallocStat.free = poolSize - netMallocStat.maximum; + + netMallocStat.pool = poolSize - 0x20; // On Vantage Master Portable this is slightly (32 bytes) smaller than the poolSize arg when tested with JPCSP + prx files + netMallocStat.maximum = 0x4050; // Dummy maximum foot print + netMallocStat.free = netMallocStat.pool; // Dummy free size, we should set this high enough to prevent any issue (ie. Vantage Master Portable), this is probably the only field being checked by games? // Clear Socket Translator Memory memset(&adhocSockets, 0, sizeof(adhocSockets)); - + + netInited = true; return hleLogSuccessI(SCENET, 0); } @@ -712,7 +771,7 @@ static void sceNetEtherStrton(u32 bufferPtr, u32 macPtr) { // 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)", statPtr); + VERBOSE_LOG(SCENET, "UNTESTED sceNetGetMallocStat(%x) at %08x", statPtr, currentMIPS->pc); if(Memory::IsValidAddress(statPtr)) Memory::WriteStruct(statPtr, &netMallocStat); else diff --git a/Core/HLE/sceNet.h b/Core/HLE/sceNet.h index 8374a878ff..3ea8b9811d 100644 --- a/Core/HLE/sceNet.h +++ b/Core/HLE/sceNet.h @@ -219,6 +219,7 @@ private: extern bool netInetInited; extern bool netApctlInited; +extern u32 netApctlState; extern SceNetApctlInfoInternal netApctlInfo; template std::string num2hex(I w, size_t hex_len = sizeof(I) << 1); diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 770302556f..f52b486cb6 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -69,11 +69,13 @@ bool netAdhocGameModeEntered; bool netAdhocMatchingInited; int netAdhocMatchingStarted = 0; -int adhocDefaultTimeout = 2000; //5000 ms -int adhocExtraPollDelayMS = 10; //10 -int adhocEventPollDelayMS = 100; //100; Seems to be the same with PSP_ADHOCCTL_RECV_TIMEOUT -int adhocMatchingEventDelayMS = 30; //30 -int adhocEventDelayMS = 300; //500; This will affect the duration of "Connecting..." dialog/message box in .Hack//Link and Naruto Ultimate Ninja Heroes 3 +int adhocDefaultTimeout = 3000000; //3000000 usec +int adhocDefaultDelay = 10000; //10000 +int adhocExtraDelay = 20000; //20000 +int adhocEventPollDelay = 100000; //100000; // Same timings with PSP_ADHOCCTL_RECV_TIMEOUT ? +int adhocMatchingEventDelay = 30000; //30000 +int adhocEventDelay = 1000000; //1000000 +u32 defaultLastRecvDelta = 10000; //10000 usec worked well for games published by Falcom (ie. Ys vs Sora Kiseki, Vantage Master Portable) SceUID threadAdhocID; @@ -85,6 +87,7 @@ std::vector matchingThreads; int IsAdhocctlInCB = 0; int adhocctlNotifyEvent = -1; +int adhocctlStateEvent = -1; int adhocSocketNotifyEvent = -1; std::map adhocctlRequests; std::map adhocSocketRequests; @@ -100,7 +103,7 @@ u32_le matchingThreadCode[3]; int matchingEventThread(int matchingId); int matchingInputThread(int matchingId); int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEtherAddr* addr, u16_le* port); -int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout); +int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout, int nonblock); int FlushPtpSocket(int socketId); int NetAdhocctl_ExitGameMode(); static int sceNetAdhocPdpSend(int id, const char* mac, u32 port, void* data, int len, int timeout, int flag); @@ -164,7 +167,7 @@ static void __GameModeNotify(u64 userdata, int cyclesLate) { while (IsGameModeActive() && IsSocketReady(sock->data.pdp.id, true, false) > 0) { SceNetEtherAddr sendermac; s32_le senderport = ADHOC_GAMEMODE_PORT; - s32_le bufsz = GAMEMODE_BUFFER_SIZE; + s32_le bufsz = uid; // GAMEMODE_BUFFER_SIZE; int ret = sceNetAdhocPdpRecv(gameModeSocket, &sendermac, &senderport, gameModeBuffer, &bufsz, 0, ADHOC_F_NONBLOCK); if (ret >= 0 && bufsz > 0) { for (auto& gma : replicaGameModeAreas) { @@ -183,7 +186,7 @@ static void __GameModeNotify(u64 userdata, int cyclesLate) { CoreTiming::ScheduleEvent(usToCycles(GAMEMODE_UPDATE_INTERVAL) - cyclesLate, gameModeNotifyEvent, userdata); return; } - INFO_LOG(SCENET, "GameMode Scheduler (%d) has finished", uid); + INFO_LOG(SCENET, "GameMode Scheduler (%d, %d) has finished", gameModeSocket, uid); } static void __AdhocctlNotify(u64 userdata, int cyclesLate) { @@ -197,10 +200,10 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { if (waitID == 0 || error != 0) return; - // Socket not found?! Should never happened! but if it ever happen should we just exit here or need to wake the thread first? + // Socket not found?! Should never happen! But if it ever happened (ie. loaded from SaveState where adhocctlRequests got cleared) return BUSY and let the game try again. if (adhocctlRequests.find(uid) == adhocctlRequests.end()) { WARN_LOG(SCENET, "sceNetAdhocctl Socket WaitID(%i) not found!", uid); - //__KernelResumeThreadFromWait(threadID, ERROR_NET_ADHOCCTL_BUSY); + __KernelResumeThreadFromWait(threadID, ERROR_NET_ADHOCCTL_BUSY); return; } @@ -223,38 +226,39 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { break; } - // Send Packet if it wasn't succesfully sent before - if (len > 0) { - if (IsSocketReady(metasocket, false, true) > 0) { - int ret = send(metasocket, (const char*)&packet, len, MSG_NOSIGNAL); - int sockerr = errno; - - if (ret > 0 || (ret == SOCKET_ERROR && sockerr != EAGAIN && sockerr != EWOULDBLOCK)) { - // Prevent from sending again - req.opcode = 0; - if (ret == SOCKET_ERROR) - DEBUG_LOG(SCENET, "sceNetAdhocctl[%i]: Socket Error (%i)", uid, sockerr); + if (g_Config.bEnableWlan) { + // Send Packet if it wasn't succesfully sent before + int ret = 0; + int sockerr = 0; + if (len > 0) { + ret = SOCKET_ERROR; + sockerr = EAGAIN; + if (IsSocketReady(metasocket, false, true) > 0) { + ret = send(metasocket, (const char*)&packet, len, MSG_NOSIGNAL); + sockerr = errno; + // Successfully Sent or Connection has been closed or Connection failure occurred + if (ret >= 0 || (ret == SOCKET_ERROR && sockerr != EAGAIN && sockerr != EWOULDBLOCK)) { + // Prevent from sending again + req.opcode = 0; + if (ret == SOCKET_ERROR) + DEBUG_LOG(SCENET, "sceNetAdhocctl[%i]: Socket Error (%i)", uid, sockerr); + } } } - } - // Now we just need to wait for replies from adhoc server and the change of state - int waitVal = __KernelGetWaitValue(threadID, error); - if (adhocctlState != waitVal && error == 0) { - // Detecting Adhocctl Initialization using waitVal < 0 - if (waitVal >= 0 || (waitVal < 0 && (g_Config.bEnableWlan && !networkInited))) { - u64 now = (u64)(time_now_d() * 1000.0); - if (now - adhocctlStartTime <= adhocDefaultTimeout) { - // Try again in another 0.5ms until state matched or timedout. + 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. CoreTiming::ScheduleEvent(usToCycles(500) - cyclesLate, adhocctlNotifyEvent, userdata); return; } else - result = 0; // ERROR_NET_ADHOCCTL_BUSY + result = ERROR_NET_ADHOCCTL_BUSY; } - else - result = 0; // Faking successfully connected to adhoc server } + else + result = ERROR_NET_ADHOCCTL_WLAN_SWITCH_OFF; __KernelResumeThreadFromWait(threadID, result); DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhocctl - State: %d", waitID, error, (int)result, adhocctlState); @@ -263,8 +267,30 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { adhocctlRequests.erase(uid); } -int WaitAdhocctlState(AdhocctlRequest request, int state, int usec, const char* reason) { - int uid = (state < 0) ? 1 : metasocket; +static void __AdhocctlState(u64 userdata, int cyclesLate) { + SceUID threadID = userdata >> 32; + int uid = (int)(userdata & 0xFFFFFFFF); + int event = uid - 1; + + s64 result = 0; + u32 error = 0; + + SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error); + if (waitID == 0 || error != 0) + return; + + u32 waitVal = __KernelGetWaitValue(threadID, error); + if (error == 0) { + adhocctlState = waitVal; + } + + __KernelResumeThreadFromWait(threadID, result); + DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhocctl - Event: %d, State: %d", waitID, error, (int)result, event, adhocctlState); +} + +// Used to simulate blocking on metasocket when send OP code to AdhocServer +int WaitBlockingAdhocctlSocket(AdhocctlRequest request, int usec, const char* reason) { + int uid = (metasocket <= 0) ? 1 : metasocket; if (adhocctlRequests.find(uid) != adhocctlRequests.end()) { WARN_LOG(SCENET, "sceNetAdhocctl - WaitID[%d] already existed, Socket is busy!", uid); @@ -275,24 +301,38 @@ int WaitAdhocctlState(AdhocctlRequest request, int state, int usec, const char* adhocctlNotifyEvent = CoreTiming::RegisterEvent("__AdhocctlNotify", __AdhocctlNotify); u64 param = ((u64)__KernelGetCurThread()) << 32 | uid; - adhocctlStartTime = (u64)(time_now_d() * 1000.0); + adhocctlStartTime = (u64)(time_now_d() * 1000000.0); adhocctlRequests[uid] = request; CoreTiming::ScheduleEvent(usToCycles(usec), adhocctlNotifyEvent, param); - __KernelWaitCurThread(WAITTYPE_NET, uid, state, 0, false, reason); + __KernelWaitCurThread(WAITTYPE_NET, uid, request.opcode, 0, false, reason); - // faked success + // Always returning a success when waiting for callback, since error code returned via callback? return 0; } -int StartGameModeScheduler() { +// Used to change Adhocctl State after a delay and before executing callback mipscall (since we don't have beforeAction) +int ScheduleAdhocctlState(int event, int newState, int usec, const char* reason) { + int uid = event + 1; + + if (adhocctlStateEvent < 0) + adhocctlStateEvent = CoreTiming::RegisterEvent("__AdhocctlState", __AdhocctlState); + + u64 param = ((u64)__KernelGetCurThread()) << 32 | uid; + CoreTiming::ScheduleEvent(usToCycles(usec), adhocctlStateEvent, param); + __KernelWaitCurThread(WAITTYPE_NET, uid, newState, 0, false, reason); + + return 0; +} + +int StartGameModeScheduler(int bufSize) { if (gameModeSocket < 0) return -1; if (gameModeNotifyEvent < 0) gameModeNotifyEvent = CoreTiming::RegisterEvent("__GameModeNotify", __GameModeNotify); - INFO_LOG(SCENET, "GameMode Scheduler (%d) has started", gameModeSocket); - u64 param = ((u64)__KernelGetCurThread()) << 32 | gameModeSocket; + INFO_LOG(SCENET, "GameMode Scheduler (%d, %d) has started", gameModeSocket, bufSize); + u64 param = ((u64)__KernelGetCurThread()) << 32 | bufSize; CoreTiming::ScheduleEvent(usToCycles(GAMEMODE_UPDATE_INTERVAL), gameModeNotifyEvent, param); return 0; @@ -342,7 +382,7 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { result = 0; } // On Windows: recvfrom on UDP can get error WSAECONNRESET when previous sendto's destination is unreachable (or destination port is not bound yet), may need to disable SIO_UDP_CONNRESET error - else if (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ECONNRESET || sockerr == ETIMEDOUT) { + else if (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ECONNRESET) { u64 now = (u64)(time_now_d() * 1000000.0); auto sock = adhocSockets[req.id - 1]; if (sock->flags & ADHOC_F_ALERTRECV) { @@ -389,7 +429,7 @@ int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTa peer = targetPeers.peers.erase(peer); } else { - if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ETIMEDOUT)) { + if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK)) { u64 now = (u64)(time_now_d() * 1000000.0); if (sock->flags & ADHOC_F_ALERTSEND) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; @@ -436,7 +476,7 @@ int DoBlockingPtpSend(int uid, AdhocSocketRequest& req, s64& result) { // Return Success result = 0; } - else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ETIMEDOUT)) { + else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK)) { u64 now = (u64)(time_now_d() * 1000000.0); if (sock->flags & ADHOC_F_ALERTSEND) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; @@ -484,7 +524,7 @@ int DoBlockingPtpRecv(int uid, AdhocSocketRequest& req, s64& result) { result = 0; } - else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ETIMEDOUT)) { + else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK)) { u64 now = (u64)(time_now_d() * 1000000.0); if (sock->flags & ADHOC_F_ALERTRECV) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; @@ -534,7 +574,7 @@ int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { if (newid > 0) result = newid; } - else if (ret == 0 || (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ETIMEDOUT))) { + else if (ret == 0 || (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK))) { u64 now = (u64)(time_now_d() * 1000000.0); if (sock->flags & ADHOC_F_ALERTACCEPT) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; @@ -545,8 +585,9 @@ int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { else if (req.timeout == 0 || now - req.startTime <= req.timeout) { return -1; } - else - result = ERROR_NET_ADHOC_TIMEOUT; + else { + result = ERROR_NET_ADHOC_TIMEOUT; + } } else result = ERROR_NET_ADHOC_INVALID_ARG; //ERROR_NET_ADHOC_TIMEOUT @@ -592,8 +633,13 @@ int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) { else if (req.timeout == 0 || now - req.startTime <= req.timeout) { return -1; } - else - result = ERROR_NET_ADHOC_TIMEOUT; + else { + // Handle Workaround that force the first Connect to be blocking for issue related to lobby or high latency networks + if (sock->nonblocking) + result = ERROR_NET_ADHOC_WOULD_BLOCK; + else + result = ERROR_NET_ADHOC_TIMEOUT; + } } else result = ERROR_NET_ADHOC_CONNECTION_REFUSED; // ERROR_NET_ADHOC_TIMEOUT; @@ -615,7 +661,7 @@ int DoBlockingPtpFlush(int uid, AdhocSocketRequest& req, s64& result) { result = 0; return 0; } - else if (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ETIMEDOUT) { + else if (sockerr == EAGAIN || sockerr == EWOULDBLOCK) { u64 now = (u64)(time_now_d() * 1000000.0); if (sock->flags & ADHOC_F_ALERTFLUSH) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; @@ -641,21 +687,32 @@ int DoBlockingPtpFlush(int uid, AdhocSocketRequest& req, s64& result) { int DoBlockingAdhocPollSocket(int uid, AdhocSocketRequest& req, s64& result) { SceNetAdhocPollSd* sds = (SceNetAdhocPollSd*)req.buffer; - int ret = PollAdhocSocket(sds, req.id, 0); + int ret = PollAdhocSocket(sds, req.id, 0, 0); if (ret <= 0) { u64 now = (u64)(time_now_d() * 1000000.0); if (req.timeout == 0 || now - req.startTime <= req.timeout) { return -1; } - else if (ret == 0) - ret = ERROR_NET_ADHOC_TIMEOUT; - else + else if (ret < 0) ret = ERROR_NET_ADHOC_EXCEPTION_EVENT; - if (ret == ERROR_NET_ADHOC_WOULD_BLOCK) - ret = ERROR_NET_ADHOC_TIMEOUT; + // FIXME: Does AdhocPollSocket can return any error code other than ERROR_NET_ADHOC_EXCEPTION_EVENT? + //else + // ret = ERROR_NET_ADHOC_TIMEOUT; } result = ret; + if (ret > 0) { + for (int i = 0; i < req.id; i++) { + if (sds[i].id > 0 && sds[i].id <= MAX_SOCKET && adhocSockets[sds[i].id - 1] != NULL) { + auto sock = adhocSockets[sds[i].id - 1]; + if (sock->type == SOCK_PTP) + VERBOSE_LOG(SCENET, "Poll PTP Socket Id: %d (%d), events: %08x, revents: %08x - state: %d", sds[i].id, sock->data.ptp.id, sds[i].events, sds[i].revents, sock->data.ptp.state); + else + VERBOSE_LOG(SCENET, "Poll PDP Socket Id: %d (%d), events: %08x, revents: %08x", sds[i].id, sock->data.pdp.id, sds[i].events, sds[i].revents); + } + } + } + return 0; } @@ -801,7 +858,7 @@ void netAdhocValidateLoopMemory() { } void __NetAdhocDoState(PointerWrap &p) { - auto s = p.Section("sceNetAdhoc", 1, 6); + auto s = p.Section("sceNetAdhoc", 1, 7); if (!s) return; @@ -875,6 +932,15 @@ void __NetAdhocDoState(PointerWrap &p) { else { gameModeNotifyEvent = -1; } + if (s >= 7) { + Do(p, adhocctlStateEvent); + if (adhocctlStateEvent != -1) { + CoreTiming::RestoreRegisterEvent(adhocctlStateEvent, "__AdhocctlState", __AdhocctlState); + } + } + else { + adhocctlStateEvent = -1; + } if (p.mode == p.MODE_READ) { // Discard leftover events @@ -918,6 +984,7 @@ void __AdhocNotifInit() { adhocctlNotifyEvent = CoreTiming::RegisterEvent("__AdhocctlNotify", __AdhocctlNotify); adhocSocketNotifyEvent = CoreTiming::RegisterEvent("__AdhocSocketNotify", __AdhocSocketNotify); gameModeNotifyEvent = CoreTiming::RegisterEvent("__GameModeNotify", __GameModeNotify); + adhocctlStateEvent = CoreTiming::RegisterEvent("__AdhocctlState", __AdhocctlState); adhocctlRequests.clear(); adhocSocketRequests.clear(); @@ -980,14 +1047,13 @@ static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { } // Need to make sure to be connected to adhoc server before returning to prevent GTA VCS failed to create/join a group and unable to see any game room - int us = adhocExtraPollDelayMS * 1000; + int us = adhocDefaultDelay; if (g_Config.bEnableWlan && !networkInited) { AdhocctlRequest dummyreq = { OPCODE_LOGIN, {0} }; - return WaitAdhocctlState(dummyreq, -1, us, "adhocctl init"); + return WaitBlockingAdhocctlSocket(dummyreq, us, "adhocctl init"); } // Give a little time for friendFinder thread to be ready before the game use the next sceNet functions, should've checked for friendFinderRunning status instead of guessing the time? - else - hleDelayResult(0, "give some time", us); + hleEatMicro(us); return 0; } @@ -1022,18 +1088,22 @@ int sceNetAdhocctlGetState(u32 ptrToStatus) { * @return Socket ID > 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_SOCKET_ID_NOT_AVAIL, ADHOC_INVALID_ADDR, ADHOC_PORT_NOT_AVAIL, ADHOC_INVALID_PORT, ADHOC_PORT_IN_USE, NET_NO_SPACE */ // When choosing AdHoc menu in Wipeout Pulse sometimes it's saying that "WLAN is turned off" on game screen and getting "kUnityCommandCode_MediaDisconnected" error in the Log Console when calling sceNetAdhocPdpCreate, probably it needed to wait something from the thread before calling this (ie. need to receives 7 bytes from adhoc server 1st?) -static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 unknown) { - INFO_LOG(SCENET, "sceNetAdhocPdpCreate(%s, %u, %u, %u) at %08x", mac2str((SceNetEtherAddr*)mac).c_str(), port, bufferSize, unknown, currentMIPS->pc); +static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 flag) { + INFO_LOG(SCENET, "sceNetAdhocPdpCreate(%s, %u, %u, %u) at %08x", mac2str((SceNetEtherAddr*)mac).c_str(), port, bufferSize, flag, currentMIPS->pc); if (!g_Config.bEnableWlan) { return -1; } - int retval = ERROR_NET_ADHOC_NOT_INITIALIZED; // Library is initialized SceNetEtherAddr * saddr = (SceNetEtherAddr *)mac; if (netAdhocInited) { // Valid Arguments are supplied if (mac != NULL && bufferSize > 0) { + // Port is in use by another PDP Socket. + // TODO: Need to test whether using the same PDP port is allowed or not, since PDP/UDP is connectionless it might be allowed. + if (isPDPPortInUse(port)) + return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_IN_USE, "port in use"); + //sport 0 should be shifted back to 0 when using offset Phantasy Star Portable 2 use this if (port == 0) port = -(int)portOffset; // Some games (ie. DBZ Shin Budokai 2) might be getting the saddr/srcmac content from SaveState and causing problems :( So we try to fix it here @@ -1042,25 +1112,15 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 u } // Valid MAC supplied if (isLocalMAC(saddr)) { - //// Unused Port supplied - //if (!_IsPDPPortInUse(port)) {} - // - //// Port is in use by another PDP Socket - //return ERROR_NET_ADHOC_PORT_IN_USE; - // Create Internet UDP Socket int usocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Valid Socket produced if (usocket != INVALID_SOCKET) { - // Change socket buffer size. - int pdpbufsize = std::max(bufferSize, PSP_ADHOC_PDP_MFS); //bufferSize*10; + // Change socket buffer size to be consistent on all platforms. // Send Buffer should be smaller than Recv Buffer to prevent faster device from flooding slower device too much. - setSockBufferSize(usocket, SO_SNDBUF, pdpbufsize); + setSockBufferSize(usocket, SO_SNDBUF, bufferSize*2); //PSP_ADHOC_PDP_MFS // Recv Buffer should be equal or larger than Send Buffer. Using larger Recv Buffer might helped reduces dropped packets during a slowdown, but too large may cause slow performance on Warriors Orochi 2. - setSockBufferSize(usocket, SO_RCVBUF, pdpbufsize*10); - - // Enable KeepAlive - setSockKeepAlive(usocket, true); + setSockBufferSize(usocket, SO_RCVBUF, bufferSize*10); //PSP_ADHOC_PDP_MFS*10 // Ignore SIGPIPE when supported (ie. BSD/MacOS) setSockNoSIGPIPE(usocket, 1); @@ -1105,6 +1165,8 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 u // Socket Type internal->type = SOCK_PDP; + internal->nonblocking = flag; + internal->buffer_size = bufferSize; // Fill in Data internal->data.pdp.id = usocket; @@ -1122,41 +1184,40 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 u changeBlockingMode(usocket, 1); // Success - return i + 1; + return hleLogDebug(SCENET, i + 1, "success"); } // Free Memory for Internal Data free(internal); } - retval = ERROR_NET_NO_SPACE; - } - else { - retval = ERROR_NET_ADHOC_PORT_IN_USE; - if (iResult == SOCKET_ERROR) { - ERROR_LOG(SCENET, "Socket error (%i) when binding port %u", errno, ntohs(addr.sin_port)); - auto n = GetI18NCategory("Networking"); - host->NotifyUserMessage(std::string(n->T("Failed to Bind Port")) + " " + std::to_string(port + portOffset) + "\n" + std::string(n->T("Please change your Port Offset")), 3.0, 0x0000ff); - } } // Close Socket closesocket(usocket); - return retval; + + // Port not available (exclusively in use?) + if (iResult == SOCKET_ERROR) { + ERROR_LOG(SCENET, "Socket error (%i) when binding port %u", errno, ntohs(addr.sin_port)); + auto n = GetI18NCategory("Networking"); + host->NotifyUserMessage(std::string(n->T("Failed to Bind Port")) + " " + std::to_string(port + portOffset) + "\n" + std::string(n->T("Please change your Port Offset")), 3.0, 0x0000ff); + + return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_NOT_AVAIL, "port not available"); + } } // Default to No-Space Error - return ERROR_NET_NO_SPACE; + return hleLogDebug(SCENET, ERROR_NET_NO_SPACE, "net no space"); } // Invalid MAC supplied - //return ERROR_NET_ADHOC_INVALID_ADDR; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ADDR, "invalid address"); } // Invalid Arguments were supplied - return ERROR_NET_ADHOC_INVALID_ARG; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg"); } // Library is uninitialized - return ERROR_NET_ADHOC_NOT_INITIALIZED; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "adhoc not initialized"); } /** @@ -1229,6 +1290,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int // Cast Socket auto socket = adhocSockets[id - 1]; auto& pdpsocket = socket->data.pdp; + socket->nonblocking = flag; // Valid Data Buffer if (data != NULL) { @@ -1263,7 +1325,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int if (sent == SOCKET_ERROR) { // Simulate blocking behaviour with non-blocking socket - if (!flag && (error == EAGAIN || error == EWOULDBLOCK || error == ETIMEDOUT)) { + if (!flag && (error == EAGAIN || error == EWOULDBLOCK)) { u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | pdpsocket.id; if (sendTargetPeers.find(threadSocketId) != sendTargetPeers.end()) { DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Socket(%d) is Busy!", id, getLocalPort(pdpsocket.id), pdpsocket.id); @@ -1435,6 +1497,7 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * // Cast Socket auto socket = adhocSockets[id - 1]; auto& pdpsocket = socket->data.pdp; + socket->nonblocking = flag; // Valid Arguments if (saddr != NULL && port != NULL && buf != NULL && len != NULL && *len > 0) { @@ -1608,14 +1671,14 @@ int NetAdhoc_SetSocketAlert(int id, s32_le flag) { return 0; } -// Flags seems to be bitmasks of ADHOC_F_ALERT... +// Flags seems to be bitmasks of ADHOC_F_ALERT... (need more games to test this) int sceNetAdhocSetSocketAlert(int id, int flag) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocSetSocketAlert(%d, %08x) at %08x", id, flag, currentMIPS->pc); + WARN_LOG_REPORT_ONCE(sceNetAdhocSetSocketAlert, SCENET, "UNTESTED sceNetAdhocSetSocketAlert(%d, %08x) at %08x", id, flag, currentMIPS->pc); - return NetAdhoc_SetSocketAlert(id, flag); + return hleLogDebug(SCENET, NetAdhoc_SetSocketAlert(id, flag), ""); } -int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout) { +int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout, int nonblock) { //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 fd_set readfds, writefds, exceptfds; int fd; @@ -1629,26 +1692,21 @@ int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout) { auto sock = adhocSockets[sds[i].id - 1]; if (sock->type == SOCK_PTP) { fd = sock->data.ptp.id; - if (sock->data.ptp.state == ADHOC_PTP_STATE_LISTEN) - sds[i].revents |= ADHOC_EV_ACCEPT; - else if (sock->data.ptp.state == ADHOC_PTP_STATE_CLOSED) - sds[i].revents |= ADHOC_EV_CONNECT; } else { fd = sock->data.pdp.id; } if (fd > maxfd) maxfd = fd; - if (sds[i].events & ADHOC_EV_RECV) FD_SET(fd, &readfds); - if (sds[i].events & ADHOC_EV_SEND) FD_SET(fd, &writefds); - //if (sds[i].events & ADHOC_EV_ALERT) - FD_SET(fd, &exceptfds); // Does Alert can be raised on revents regardless of events bitmask? + FD_SET(fd, &readfds); + FD_SET(fd, &writefds); + FD_SET(fd, &exceptfds); } } timeval tmout; tmout.tv_sec = timeout / 1000000; // seconds tmout.tv_usec = (timeout % 1000000); // microseconds int affectedsockets = select(maxfd + 1, &readfds, &writefds, &exceptfds, &tmout); - if (affectedsockets > 0) { + if (affectedsockets >= 0) { affectedsockets = 0; for (int i = 0; i < count; i++) { if (sds[i].id > 0 && sds[i].id <= MAX_SOCKET && adhocSockets[sds[i].id - 1] != NULL) { @@ -1659,32 +1717,63 @@ int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout) { else { fd = sock->data.pdp.id; } - if (FD_ISSET(fd, &readfds)) + if ((sds[i].events & ADHOC_EV_RECV) && FD_ISSET(fd, &readfds)) sds[i].revents |= ADHOC_EV_RECV; - if (FD_ISSET(fd, &writefds)) + if ((sds[i].events & ADHOC_EV_SEND) && FD_ISSET(fd, &writefds)) sds[i].revents |= ADHOC_EV_SEND; - if (FD_ISSET(fd, &exceptfds) || sock->alerted_flags) + if (sock->alerted_flags) sds[i].revents |= ADHOC_EV_ALERT; + // Mask certain revents bits with events bits sds[i].revents &= sds[i].events; - if (sds[i].revents) affectedsockets++; + + if (sock->type == SOCK_PTP) { + // FIXME: Should we also make use "retry_interval" for ADHOC_EV_ACCEPT, similar to ADHOC_EV_CONNECT ? + if (sock->data.ptp.state == ADHOC_PTP_STATE_LISTEN && (sds[i].events & ADHOC_EV_ACCEPT) && FD_ISSET(fd, &readfds)) { + sds[i].revents |= ADHOC_EV_ACCEPT; + } + // Fate Unlimited Codes and Carnage Heart EXA relies on AdhocPollSocket in order to retry a failed PtpConnect, but the interval must not be too long (about 1 frame before state became Established by GetPtpStat) for Bleach Heat the Soul 7 to work properly. + else if ((sds[i].events & ADHOC_EV_CONNECT) && ((sock->data.ptp.state == ADHOC_PTP_STATE_CLOSED && sock->attemptCount == 0) || + (sock->data.ptp.state == ADHOC_PTP_STATE_SYN_SENT && (CoreTiming::GetGlobalTimeUsScaled() - sock->lastAttempt > 1000/*std::max(1000, sock->retry_interval - 60000)*/)))) { + + sds[i].revents |= ADHOC_EV_CONNECT; + } + // Check for socket state (already disconnected/closed by remote peer, already closed/deleted, not a socket or not opened/connected yet?) + // Raise ADHOC_EV_DISCONNECT, ADHOC_EV_DELETE, ADHOC_EV_INVALID on revents regardless of events as needed (similar to POLLHUP, POLLERR, and POLLNVAL on posix poll) + if (sock->data.ptp.state == ADHOC_PTP_STATE_CLOSED) { + if (sock->attemptCount > 0) { + sds[i].revents |= ADHOC_EV_DISCONNECT; // remote peer has closed the socket + } + } + } if (sock->flags & ADHOC_F_ALERTPOLL) { - affectedsockets = ERROR_NET_ADHOC_SOCKET_ALERTED; // FIXME: Should we clear the flag after alert signaled? sock->flags &= ~ADHOC_F_ALERTPOLL; sock->alerted_flags |= ADHOC_F_ALERTPOLL; - break; + + return ERROR_NET_ADHOC_SOCKET_ALERTED; } } + else { + sds[i].revents |= ADHOC_EV_INVALID; + } + if (sds[i].revents) affectedsockets++; } } - else if (affectedsockets < 0) { - affectedsockets = ERROR_NET_ADHOC_WOULD_BLOCK; + else { + // FIXME: Does AdhocPollSocket can return any error code other than ERROR_NET_ADHOC_EXCEPTION_EVENT on blocking/non-blocking mode? + /*if (nonblock) + affectedsockets = ERROR_NET_ADHOC_WOULD_BLOCK; + else + affectedsockets = ERROR_NET_ADHOC_TIMEOUT; + */ + affectedsockets = ERROR_NET_ADHOC_EXCEPTION_EVENT; } return affectedsockets; } int sceNetAdhocPollSocket(u32 socketStructAddr, int count, int timeout, int nonblock) { // timeout in microseconds + DEBUG_LOG_REPORT_ONCE(sceNetAdhocPollSocket, SCENET, "UNTESTED sceNetAdhocPollSocket(%08x, %i, %i, %i) at %08x", socketStructAddr, count, timeout, nonblock, currentMIPS->pc); // Library is initialized if (netAdhocInited) { @@ -1707,9 +1796,9 @@ int sceNetAdhocPollSocket(u32 socketStructAddr, int count, int timeout, int nonb // Blocking Mode else - // Does timeout = 0 means undefinite on PSP? - if (timeout == 0) - timeout = adhocDefaultTimeout * 1000; // minSocketTimeoutUS; + // Does timeout = 0 means indefinite on PSP? + if (timeout <= 0) + timeout = adhocDefaultTimeout; // minSocketTimeoutUS; if (count > (int)FD_SETSIZE) count = FD_SETSIZE; // return 0; //ERROR_NET_ADHOC_INVALID_ARG @@ -1721,7 +1810,7 @@ int sceNetAdhocPollSocket(u32 socketStructAddr, int count, int timeout, int nonb //int affectedsockets = sceNetInetPoll(isds, count, timeout); int affectedsockets = 0; if (nonblock) - affectedsockets = PollAdhocSocket(sds, count, timeout); + affectedsockets = PollAdhocSocket(sds, count, timeout, nonblock); else { // Simulate blocking behaviour with non-blocking socket // Borrowing some arguments to pass some parameters. The dummy WaitID(count+1) might not be unique thus have duplicate possibilities if there are multiple thread trying to poll the same numbers of socket at the same time @@ -1736,11 +1825,23 @@ int sceNetAdhocPollSocket(u32 socketStructAddr, int count, int timeout, int nonb // Non-blocking mode // Bleach 7 seems to use nonblocking and check the return value > 0, or 0x80410709 (ERROR_NET_ADHOC_WOULD_BLOCK), also 0x80410717 (ERROR_NET_ADHOC_EXCEPTION_EVENT), when using prx files on JPCSP it can return 0 if (affectedsockets >= 0) { - DEBUG_LOG(SCENET, "%08x=sceNetAdhocPollSocket(%08x, %i, %i, %i) at %08x", affectedsockets, socketStructAddr, count, timeout, nonblock, currentMIPS->pc); - return affectedsockets; + if (affectedsockets > 0) { + for (int i = 0; i < count; i++) { + if (sds[i].id > 0 && sds[i].id <= MAX_SOCKET && adhocSockets[sds[i].id - 1] != NULL) { + auto sock = adhocSockets[sds[i].id - 1]; + if (sock->type == SOCK_PTP) + VERBOSE_LOG(SCENET, "Poll PTP Socket Id: %d (%d), events: %08x, revents: %08x - state: %d", sds[i].id, sock->data.ptp.id, sds[i].events, sds[i].revents, sock->data.ptp.state); + else + VERBOSE_LOG(SCENET, "Poll PDP Socket Id: %d (%d), events: %08x, revents: %08x", sds[i].id, sock->data.pdp.id, sds[i].events, sds[i].revents); + } + } + } + // Workaround to get 30 FPS instead of the too fast 60 FPS on Fate Unlimited Codes, it's abit absurd for a non-blocking call to have this much delay tho, and hleDelayResult doesn't works as good as hleEatMicro for this workaround. + hleEatMicro(1000); // hleEatMicro(7500); // normally 1ms, but using 7.5ms here seems to show better result for Bleach Heat the Soul 7 and other games with too high FPS, but may have a risk of slowing down games that already runs at normal FPS? (need more games to test this) + return hleLogDebug(SCENET, affectedsockets, "success"); } - else if (nonblock && affectedsockets < 0) - return hleLogDebug(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block"); + //else if (nonblock && affectedsockets < 0) + // return hleLogDebug(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block"); // Is this error code valid for PollSocket? as it always returns 0 even when nonblock flag is set return hleLogDebug(SCENET, ERROR_NET_ADHOC_EXCEPTION_EVENT, "exception event"); } @@ -1846,7 +1947,7 @@ int sceNetAdhocctlScan() { // Library initialized if (netAdhocctlInited) { - int us = adhocEventPollDelayMS * 1000; + int us = adhocDefaultDelay * 2; // Only scan when in Disconnected state, otherwise AdhocServer will kick you out if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED) { @@ -1874,13 +1975,14 @@ int sceNetAdhocctlScan() { } else if (friendFinderRunning) { AdhocctlRequest req = { OPCODE_SCAN, {0} }; - return WaitAdhocctlState(req, ADHOCCTL_STATE_DISCONNECTED, us, "adhocctl scan"); + return WaitBlockingAdhocctlSocket(req, us, "adhocctl scan"); } } else { // Return Success and let friendFinder thread to notify the handler when scan completed // Not delaying here may cause Naruto Shippuden Ultimate Ninja Heroes 3 to get disconnected when the mission started - return hleDelayResult(0, "give a little time to be ready to receive the callback", us); + hleEatMicro(us); + return 0; } } else if (adhocctlState == ADHOCCTL_STATE_SCANNING) @@ -1890,7 +1992,8 @@ int sceNetAdhocctlScan() { // We need to notify the handler on success, even if it was faked notifyAdhocctlHandlers(ADHOCCTL_EVENT_SCAN, 0); // FIXME: returning ERROR_NET_ADHOCCTL_BUSY may trigger the game (ie. Ford Street Racing) to call sceNetAdhocctlDisconnect, But Not returning a Success(0) will cause Valhalla Knights 2 not working properly - return hleDelayResult(0, "give a little time to be ready to receive the callback", us); + hleEatMicro(us); + return 0; } // Library uninitialized @@ -2034,7 +2137,7 @@ u32 NetAdhocctl_Disconnect() { // Library initialized if (netAdhocctlInited) { int iResult, error; - int us = adhocEventPollDelayMS * 1000; + int us = adhocDefaultDelay * 5; // Connected State (Adhoc Mode) if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED) { // (threadStatus == ADHOCCTL_STATE_CONNECTED) // Clear Network Name @@ -2062,7 +2165,7 @@ u32 NetAdhocctl_Disconnect() { } else if (friendFinderRunning) { AdhocctlRequest req = { OPCODE_DISCONNECT, {0} }; - WaitAdhocctlState(req, ADHOCCTL_STATE_DISCONNECTED, us, "adhocctl disconnect"); + WaitBlockingAdhocctlSocket(req, us, "adhocctl disconnect"); } else { // Set Disconnected State @@ -2099,6 +2202,7 @@ u32 NetAdhocctl_Disconnect() { notifyAdhocctlHandlers(ADHOCCTL_EVENT_DISCONNECT, 0); //hleCheckCurrentCallbacks(); + hleEatMicro(us); // Return Success, some games might ignore returned value and always treat it as success, otherwise repeatedly calling this function return 0; } @@ -2159,12 +2263,13 @@ int NetAdhocctl_Term() { shutdown(metasocket, SD_BOTH); closesocket(metasocket); metasocket = (int)INVALID_SOCKET; - // Delete fake PSP Thread - if (threadAdhocID != 0) { + // Delete fake PSP Thread. + // kernelObjects may already been cleared early during a Shutdown, thus trying to access it may generates Warning/Error in the log + if (threadAdhocID > 0 && strcmp(__KernelGetThreadName(threadAdhocID), "ERROR") != 0) { __KernelStopThread(threadAdhocID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocThread stopped"); __KernelDeleteThread(threadAdhocID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocThread deleted"); - threadAdhocID = 0; } + threadAdhocID = 0; adhocctlCurrentMode = ADHOCCTL_MODE_NONE; netAdhocctlInited = false; } @@ -2177,8 +2282,10 @@ int sceNetAdhocctlTerm() { INFO_LOG(SCENET, "sceNetAdhocctlTerm()"); //if (netAdhocMatchingInited) NetAdhocMatching_Term(); + int retval = NetAdhocctl_Term(); - return NetAdhocctl_Term(); + hleEatMicro(adhocDefaultDelay); + return retval; } static int sceNetAdhocctlGetNameByAddr(const char *mac, u32 nameAddr) { @@ -2217,7 +2324,7 @@ static int sceNetAdhocctlGetNameByAddr(const char *mac, u32 nameAddr) { for (; peer != NULL; peer = peer->next) { // Match found - if (isMacMatch(&peer->mac_addr, (const SceNetEtherAddr*)mac)) + if (peer->last_recv != 0 && isMacMatch(&peer->mac_addr, (const SceNetEtherAddr*)mac)) { // Write Data *nickname = peer->nickname; @@ -2263,7 +2370,7 @@ int sceNetAdhocctlGetPeerInfo(const char *mac, int size, u32 peerInfoAddr) { if (netAdhocctlInited) { if ((size < (int)sizeof(SceNetAdhocctlPeerInfoEmu)) || (buf == NULL)) return ERROR_NET_ADHOCCTL_INVALID_ARG; - int retval = ERROR_NET_ADHOCCTL_INVALID_ARG; // -1; + int retval = ERROR_NET_ADHOC_NO_ENTRY; // -1; // Local MAC if (isLocalMAC(maddr)) { @@ -2276,7 +2383,7 @@ int sceNetAdhocctlGetPeerInfo(const char *mac, int size, u32 peerInfoAddr) { buf->mac_addr = *maddr; buf->flags = 0x0400; buf->padding = 0; - buf->last_recv = CoreTiming::GetGlobalTimeUsScaled() - 1; + buf->last_recv = CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta; // Success retval = 0; @@ -2288,12 +2395,11 @@ int sceNetAdhocctlGetPeerInfo(const char *mac, int size, u32 peerInfoAddr) { peerlock.lock(); SceNetAdhocctlPeerInfo * peer = findFriend(maddr); - if (peer != NULL) { + if (peer != NULL && peer->last_recv != 0) { // Fake Receive Time - if (peer->last_recv != 0) - peer->last_recv = CoreTiming::GetGlobalTimeUsScaled() - 1; + peer->last_recv = std::max(peer->last_recv, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta); - //buf->next = 0; + buf->next = 0; buf->nickname = peer->nickname; buf->nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char buf->mac_addr = *maddr; @@ -2369,16 +2475,14 @@ int NetAdhocctl_Create(const char* groupName) { //setConnectionStatus(1); // Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session - int us = adhocEventDelayMS * 1000; + int us = adhocDefaultDelay; if (adhocctlState != ADHOCCTL_STATE_CONNECTED && iResult == SOCKET_ERROR && friendFinderRunning) { AdhocctlRequest req = { OPCODE_CONNECT, {0} }; if (groupNameStruct != NULL) req.group = *groupNameStruct; - return WaitAdhocctlState(req, ADHOCCTL_STATE_CONNECTED, us, "adhocctl connect"); + return WaitBlockingAdhocctlSocket(req, us, "adhocctl connect"); } - // Giving time for Naruto Shippuden Ninja Heroes 3 to close down the "Connecting..." dialog, otherwise the dialog will stuck there. - else if (adhocctlState == ADHOCCTL_STATE_CONNECTED || iResult != SOCKET_ERROR) - hleDelayResult(0, "give time to init/cleanup", us); + hleEatMicro(us); // Return Success return 0; } @@ -2461,17 +2565,13 @@ int sceNetAdhocctlJoin(u32 scanInfoAddr) { int NetAdhocctl_CreateEnterGameMode(const char* group_name, int game_type, int num_members, u32 membersAddr, u32 timeout, int flag) { if (!netAdhocctlInited) - return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized"); + return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; if (!Memory::IsValidAddress(membersAddr)) - return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg"); + return ERROR_NET_ADHOCCTL_INVALID_ARG; if (game_type <= 0 || game_type > 3 || num_members < 2 || num_members > 16 || (game_type == 1 && num_members > 4)) - return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg"); - - if (gameModeBuffer) - free(gameModeBuffer); - gameModeBuffer = (u8*)malloc(GAMEMODE_BUFFER_SIZE); + return ERROR_NET_ADHOCCTL_INVALID_ARG; SceNetEtherAddr* addrs = PSPPointer::Create(membersAddr); // List of participating MAC addresses (started from host) gameModeMacs.clear(); @@ -2511,7 +2611,7 @@ static int sceNetAdhocctlCreateEnterGameMode(const char * group_name, int game_t memcpy(grpName, group_name, ADHOCCTL_GROUPNAME_LEN); // For logging purpose, must not be truncated WARN_LOG_REPORT_ONCE(sceNetAdhocctlCreateEnterGameMode, SCENET, "UNTESTED sceNetAdhocctlCreateEnterGameMode(%s, %i, %i, %08x, %i, %i) at %08x", grpName, game_type, num_members, membersAddr, timeout, flag, currentMIPS->pc); - return NetAdhocctl_CreateEnterGameMode(group_name, game_type, num_members, membersAddr, timeout, flag); + return hleLogDebug(SCENET, NetAdhocctl_CreateEnterGameMode(group_name, game_type, num_members, membersAddr, timeout, flag), ""); } /** @@ -2536,17 +2636,13 @@ static int sceNetAdhocctlJoinEnterGameMode(const char * group_name, const char * if (!hostMac) return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg"); - if (gameModeBuffer) - free(gameModeBuffer); - gameModeBuffer = (u8*)malloc(GAMEMODE_BUFFER_SIZE); - // Add host mac first gameModeMacs.push_back(*(SceNetEtherAddr*)hostMac); adhocctlCurrentMode = ADHOCCTL_MODE_GAMEMODE; adhocConnectionType = ADHOC_JOIN; netAdhocGameModeEntered = true; - return NetAdhocctl_Create(group_name); + return hleLogDebug(SCENET, NetAdhocctl_Create(group_name), ""); } /** @@ -2566,7 +2662,7 @@ int sceNetAdhocctlCreateEnterGameModeMin(const char *group_name, int game_type, memcpy(grpName, group_name, ADHOCCTL_GROUPNAME_LEN); // For logging purpose, must not be truncated WARN_LOG_REPORT_ONCE(sceNetAdhocctlCreateEnterGameModeMin, SCENET, "UNTESTED sceNetAdhocctlCreateEnterGameModeMin(%s, %i, %i, %i, %08x, %d, %i) at %08x", grpName, game_type, min_members, num_members, membersAddr, timeout, flag, currentMIPS->pc); // We don't really need the Minimum User Check - return NetAdhocctl_CreateEnterGameMode(group_name, game_type, num_members, membersAddr, timeout, flag); + return hleLogDebug(SCENET, NetAdhocctl_CreateEnterGameMode(group_name, game_type, num_members, membersAddr, timeout, flag), ""); } int NetAdhoc_Term() { @@ -2605,8 +2701,10 @@ int NetAdhoc_Term() { 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(); - return hleLogSuccessInfoI(SCENET, NetAdhoc_Term()); + hleEatMicro(adhocDefaultDelay); + return hleLogSuccessInfoI(SCENET, retval); } static int sceNetAdhocGetPdpStat(u32 structSize, u32 structAddr) { @@ -2628,7 +2726,7 @@ static int sceNetAdhocGetPdpStat(u32 structSize, u32 structAddr) { { // Return Required Size *buflen = sizeof(SceNetAdhocPdpStat) * socketcount; - VERBOSE_LOG(SCENET, "PDP Socket Count: %d", socketcount); + VERBOSE_LOG(SCENET, "Stat PDP Socket Count: %d", socketcount); // Success return 0; @@ -2650,19 +2748,15 @@ static int sceNetAdhocGetPdpStat(u32 structSize, u32 structAddr) { // Valid Socket Entry auto sock = adhocSockets[j]; if (sock != NULL && sock->type == SOCK_PDP) { + // Set available bytes to be received. With FIOREAD There might be lingering 1 byte in recv buffer when remote peer's socket got closed (ie. Warriors Orochi 2) + sock->data.pdp.rcv_sb_cc = getAvailToRecv(sock->data.pdp.id); + // Copy Socket Data from Internal Memory memcpy(&buf[i], &sock->data.pdp, sizeof(SceNetAdhocPdpStat)); // Fix Client View Socket ID buf[i].id = j + 1; - // Set available bytes to be received. With FIOREAD There might be lingering 1 byte in recv buffer when remote peer's socket got closed - u32 avail = 0; - if (IsSocketReady(sock->data.pdp.id, true, false) > 0) { - avail = getAvailToRecv(sock->data.pdp.id); - } - buf[i].rcv_sb_cc = avail; - // Write End of List Reference buf[i].next = 0; @@ -2670,7 +2764,7 @@ static int sceNetAdhocGetPdpStat(u32 structSize, u32 structAddr) { if (i > 0) buf[i - 1].next = structAddr + (i * sizeof(SceNetAdhocPdpStat)); - VERBOSE_LOG(SCENET, "PDP Socket Id: %d, LPort: %d, RecvSbCC: %d", buf[i].id, buf[i].lport, buf[i].rcv_sb_cc); + VERBOSE_LOG(SCENET, "Stat PDP Socket Id: %d (%d), LPort: %d, RecvSbCC: %d", buf[i].id, sock->data.pdp.id, buf[i].lport, buf[i].rcv_sb_cc); // Increment Counter i++; @@ -2717,7 +2811,7 @@ static int sceNetAdhocGetPtpStat(u32 structSize, u32 structAddr) { if (buflen != NULL && buf == NULL) { // Return Required Size *buflen = sizeof(SceNetAdhocPtpStat) * socketcount; - VERBOSE_LOG(SCENET, "PTP Socket Count: %d", socketcount); + VERBOSE_LOG(SCENET, "Stat PTP Socket Count: %d", socketcount); // Success return 0; @@ -2737,15 +2831,24 @@ static int sceNetAdhocGetPtpStat(u32 structSize, u32 structAddr) { // Valid Socket Entry auto sock = adhocSockets[j]; if ( sock != NULL && sock->type == SOCK_PTP) { + // Update connection state. + // GvG Next Plus relies on GetPtpStat to determine if Connection has been Established or not, but should not be updated too long for GvG to work, and should not be updated too fast(need to be 1 frame after PollSocket checking for ADHOC_EV_CONNECT) for Bleach Heat the Soul 7 to work properly. + if ((sock->data.ptp.state == ADHOC_PTP_STATE_SYN_SENT || sock->data.ptp.state == ADHOC_PTP_STATE_SYN_RCVD) && (CoreTiming::GetGlobalTimeUsScaled() - sock->lastAttempt > 35000/*sock->retry_interval*/)) { + // FIXME: May be we should poll all of them together on a single poll call instead of each socket separately? + if (IsSocketReady(sock->data.ptp.id, true, true) > 0) { + sock->data.ptp.state = ADHOC_PTP_STATE_ESTABLISHED; + } + } + + // Set available bytes to be received + sock->data.ptp.rcv_sb_cc = getAvailToRecv(sock->data.ptp.id); + // Copy Socket Data from internal Memory memcpy(&buf[i], &sock->data.ptp, sizeof(SceNetAdhocPtpStat)); // Fix Client View Socket ID buf[i].id = j + 1; - // Set available bytes to be received - buf[i].rcv_sb_cc = getAvailToRecv(sock->data.ptp.id); - // Write End of List Reference buf[i].next = 0; @@ -2753,7 +2856,7 @@ static int sceNetAdhocGetPtpStat(u32 structSize, u32 structAddr) { if (i > 0) buf[i - 1].next = structAddr + (i * sizeof(SceNetAdhocPtpStat)); - VERBOSE_LOG(SCENET, "PTP Socket Id: %d, LPort: %d, RecvSbCC: %d", buf[i].id, buf[i].lport, buf[i].rcv_sb_cc); + VERBOSE_LOG(SCENET, "Stat PTP Socket Id: %d (%d), LPort: %d, RecvSbCC: %d, State: %d", buf[i].id, sock->data.ptp.id, buf[i].lport, buf[i].rcv_sb_cc, buf[i].state); // Increment Counter i++; @@ -2763,6 +2866,7 @@ static int sceNetAdhocGetPtpStat(u32 structSize, u32 structAddr) { // Update Buffer Length *buflen = i * sizeof(SceNetAdhocPtpStat); + hleEatMicro(1000); // Not sure how long it takes, since GetPtpStat didn't get logged when using prx files on JPCSP // Success return 0; } @@ -2788,10 +2892,10 @@ static int sceNetAdhocGetPtpStat(u32 structSize, u32 structAddr) { * @param flag Bitflags (Unused) * @return Socket ID > 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_ADDR, ADHOC_INVALID_PORT */ -static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, int dport, int bufsize, int rexmt_int, int rexmt_cnt, int unknown) { - INFO_LOG(SCENET, "sceNetAdhocPtpOpen(%s, %d, %s, %d, %d, %d, %d, %d) at %08x", mac2str((SceNetEtherAddr*)srcmac).c_str(), sport, mac2str((SceNetEtherAddr*)dstmac).c_str(),dport,bufsize, rexmt_int, rexmt_cnt, unknown, currentMIPS->pc); +static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, int dport, int bufsize, int rexmt_int, int rexmt_cnt, int flag) { + INFO_LOG(SCENET, "sceNetAdhocPtpOpen(%s, %d, %s, %d, %d, %d, %d, %d) at %08x", mac2str((SceNetEtherAddr*)srcmac).c_str(), sport, mac2str((SceNetEtherAddr*)dstmac).c_str(),dport,bufsize, rexmt_int, rexmt_cnt, flag, currentMIPS->pc); if (!g_Config.bEnableWlan) { - return 0; + return -1; } SceNetEtherAddr* saddr = (SceNetEtherAddr*)srcmac; SceNetEtherAddr* daddr = (SceNetEtherAddr*)dstmac; @@ -2804,6 +2908,10 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, } // Valid Addresses if (saddr != NULL && isLocalMAC(saddr) && daddr != NULL && !isBroadcastMAC(daddr)) { + // Dissidia 012 will try to reOpen the port without Closing the old one first when PtpConnect failed to try again. + if (isPTPPortInUse(sport, false)) + return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_IN_USE, "port in use"); + // Random Port required if (sport == 0) { isClient = true; @@ -2819,9 +2927,8 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, // Valid Socket produced if (tcpsocket > 0) { // Change socket buffer size to be consistent on all platforms. - int ptpbufsize = std::max(bufsize, PSP_ADHOC_PTP_MSS); - setSockBufferSize(tcpsocket, SO_SNDBUF, ptpbufsize); - setSockBufferSize(tcpsocket, SO_RCVBUF, ptpbufsize*10); + setSockBufferSize(tcpsocket, SO_SNDBUF, bufsize*2); //PSP_ADHOC_PTP_MFS + setSockBufferSize(tcpsocket, SO_RCVBUF, bufsize*10); //PSP_ADHOC_PTP_MFS*10 // Enable KeepAlive setSockKeepAlive(tcpsocket, true, rexmt_int / 1000000L, rexmt_cnt); @@ -2873,8 +2980,10 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, // Socket Type internal->type = SOCK_PTP; - internal->send_timeout = rexmt_int; + internal->retry_interval = rexmt_int; internal->retry_count = rexmt_cnt; + internal->nonblocking = flag; + internal->buffer_size = bufsize; // Copy Infrastructure Socket ID internal->data.ptp.id = tcpsocket; @@ -2897,7 +3006,7 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, changeBlockingMode(tcpsocket, 1); // Return PTP Socket Pointer - return i + 1; + return hleLogDebug(SCENET, i + 1, "success"); } // Free Memory @@ -2914,21 +3023,23 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, closesocket(tcpsocket); // Port not available (exclusively in use?) - return ERROR_NET_ADHOC_INVALID_PORT; // ERROR_NET_ADHOC_PORT_IN_USE; // ERROR_NET_ADHOC_PORT_NOT_AVAIL; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_NOT_AVAIL, "port not available"); // ERROR_NET_ADHOC_PORT_IN_USE; // ERROR_NET_ADHOC_INVALID_PORT; } } // Invalid Arguments - return ERROR_NET_ADHOC_INVALID_ARG; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg"); } // Invalid Addresses - return ERROR_NET_ADHOC_INVALID_ARG; // ERROR_NET_ADHOC_INVALID_ADDR; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ADDR, "invalid address"); // ERROR_NET_ADHOC_INVALID_ARG; } - return 0; + // Library is uninitialized + return hleLogDebug(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "adhoc not initialized"); } +// On a POSIX accept, returned socket may inherits properties from the listening socket, does PtpAccept also have similar behavior? int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEtherAddr* addr, u16_le* port) { // Cast Socket auto socket = adhocSockets[ptpId - 1]; @@ -2940,9 +3051,6 @@ int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEther // Enable Port Re-use setSockReuseAddrPort(newsocket); - // Enable KeepAlive - setSockKeepAlive(newsocket, true, socket->recv_timeout / 1000000L, socket->retry_count); - // Disable Nagle Algo to send immediately. Or may be we shouldn't disable Nagle since there is PtpFlush function? if (g_Config.bTCPNoDelay) setSockNoDelay(newsocket, 1); @@ -2973,15 +3081,23 @@ int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEther // Clear Memory memset(internal, 0, sizeof(AdhocSocket)); + // Inherits some of Listening socket's properties // Socket Type internal->type = SOCK_PTP; + internal->nonblocking = socket->nonblocking; + internal->attemptCount = 1; // Used to differentiate between closed state of disconnected socket and not connected yet. + internal->retry_interval = socket->retry_interval; + internal->retry_count = socket->retry_count; + // Enable KeepAlive + setSockKeepAlive(newsocket, true, internal->retry_interval / 1000000L, internal->retry_count); // Copy Socket Descriptor to Structure internal->data.ptp.id = newsocket; - // Set Default Buffer Size - setSockBufferSize(newsocket, SO_SNDBUF, PSP_ADHOC_PTP_MSS); - setSockBufferSize(newsocket, SO_RCVBUF, PSP_ADHOC_PTP_MSS*10); + // Set Default Buffer Size or inherit the size? + internal->buffer_size = socket->buffer_size; + setSockBufferSize(newsocket, SO_SNDBUF, internal->buffer_size*2); //PSP_ADHOC_PTP_MSS + setSockBufferSize(newsocket, SO_RCVBUF, internal->buffer_size*10); //PSP_ADHOC_PTP_MSS*10 // Copy Local Address Data to Structure getLocalMac(&internal->data.ptp.laddr); @@ -2991,7 +3107,7 @@ int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEther internal->data.ptp.paddr = mac; internal->data.ptp.pport = ntohs(peeraddr.sin_port) - portOffset; - // Set Connected State + // Set Connection State internal->data.ptp.state = ADHOC_PTP_STATE_ESTABLISHED; // Return Peer Address Information @@ -3008,7 +3124,7 @@ int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEther // Switch to non-blocking for futher usage changeBlockingMode(newsocket, 1); - INFO_LOG(SCENET, "sceNetAdhocPtpAccept[%i->%i:%u]: Established (%s:%u)", ptpId, i + 1, internal->data.ptp.lport, inet_ntoa(peeraddr.sin_addr), internal->data.ptp.pport); + INFO_LOG(SCENET, "sceNetAdhocPtpAccept[%i->%i:%u]: Established (%s:%u) - state: %d", ptpId, i + 1, internal->data.ptp.lport, inet_ntoa(peeraddr.sin_addr), internal->data.ptp.pport, internal->data.ptp.state); // Return Socket return i + 1; @@ -3052,7 +3168,7 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int VERBOSE_LOG(SCENET, "sceNetAdhocPtpAccept(%d, [%08x]=%s, [%08x]=%u, %d, %u) at %08x", id, peerMacAddrPtr, mac2str(addr).c_str(), peerPortPtr, port ? *port : -1, timeout, flag, currentMIPS->pc); } if (!g_Config.bEnableWlan) { - return 0; + return -1; } // Library is initialized @@ -3064,6 +3180,7 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int // Cast Socket auto socket = adhocSockets[id - 1]; auto& ptpsocket = socket->data.ptp; + socket->nonblocking = flag; // Listener Socket if (ptpsocket.state == ADHOC_PTP_STATE_LISTEN) { @@ -3082,11 +3199,10 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int } if (newsocket == 0 || (newsocket == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK || error == ETIMEDOUT))) { - socket->attemptCount++; if (flag == 0) { // Simulate blocking behaviour with non-blocking socket u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id; - return WaitBlockingAdhocSocket(threadSocketId, PTP_ACCEPT, id, nullptr, nullptr, (flag) ? socket->recv_timeout : timeout, addr, port, "ptp accept"); + return WaitBlockingAdhocSocket(threadSocketId, PTP_ACCEPT, id, nullptr, nullptr, timeout, addr, port, "ptp accept"); } // Prevent spamming Debug Log with retries of non-bocking socket else { @@ -3096,7 +3212,6 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int // Accepted New Connection if (newsocket > 0) { - socket->attemptCount++; int newid = AcceptPtpSocket(id, newsocket, peeraddr, addr, port); if (newid >= 0) return newid; @@ -3147,13 +3262,14 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { // Cast Socket auto socket = adhocSockets[id - 1]; auto& ptpsocket = socket->data.ptp; + socket->nonblocking = flag; // Phantasy Star Portable 2 will try to reconnect even when previous connect already success, so we should return success too if it's already connected if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED) return 0; // Valid Client Socket - if (ptpsocket.state == ADHOC_PTP_STATE_CLOSED) { + if (ptpsocket.state == ADHOC_PTP_STATE_CLOSED || ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT) { // Target Address sockaddr_in sin; memset(&sin, 0, sizeof(sin)); @@ -3168,24 +3284,24 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { // Some games (ie. PSP2) might try to talk to it's self, not sure if they talked through WAN or LAN when using public Adhoc Server tho sin.sin_port = htons(ptpsocket.pport + ((isOriPort && !isPrivateIP(sin.sin_addr.s_addr)) ? 0 : portOffset)); - // Grab Nonblocking Flag - //uint32_t nbio = getNonBlockingFlag(socket->id); - // Connect Socket to Peer // NOTE: Based on what i read at stackoverflow, The First Non-blocking POSIX connect will always returns EAGAIN/EWOULDBLOCK because it returns without waiting for ACK/handshake, But GvG Next Plus is treating non-blocking PtpConnect just like blocking connect, May be on a real PSP the first non-blocking sceNetAdhocPtpConnect can be successfull? - // TODO: Keep track number of Connect attempts so we can simulate blocking on first attempt (getNonBlockingFlag can't be used to get non-blocking flag on Windows thus can't be used to keep track) int connectresult = connect(ptpsocket.id, (sockaddr *)&sin, sizeof(sin)); // Grab Error Code int errorcode = errno; if (connectresult == SOCKET_ERROR) { - ERROR_LOG(SCENET, "sceNetAdhocPtpConnect[%i]: Socket Error (%i) to %s:%u", id, errorcode, inet_ntoa(sin.sin_addr), ptpsocket.pport); + if (errorcode == EAGAIN || errorcode == EWOULDBLOCK || errorcode == EALREADY || errorcode == EISCONN) + DEBUG_LOG(SCENET, "sceNetAdhocPtpConnect[%i]: Socket Error (%i) to %s:%u", id, errorcode, inet_ntoa(sin.sin_addr), ptpsocket.pport); + else + ERROR_LOG(SCENET, "sceNetAdhocPtpConnect[%i]: Socket Error (%i) to %s:%u", id, errorcode, inet_ntoa(sin.sin_addr), ptpsocket.pport); } // Instant Connection (Lucky!) if (connectresult != SOCKET_ERROR || errorcode == EISCONN) { socket->attemptCount++; + socket->lastAttempt = CoreTiming::GetGlobalTimeUsScaled(); // Set Connected State ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED; @@ -3196,17 +3312,19 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { // Connection in Progress else if (connectresult == SOCKET_ERROR && connectInProgress(errorcode)) { + socket->data.ptp.state = ADHOC_PTP_STATE_SYN_SENT; socket->attemptCount++; - // Nonblocking Mode. First attempt need to be blocking for GvG Next Plus to work, even though it used non-blocking flag but only try to connect once per socket, which mean treating it just like blocking socket instead of non-blocking :( - if (flag && socket->attemptCount > 1) { - //if (errorcode == EALREADY) return ERROR_NET_ADHOC_BUSY; - return ERROR_NET_ADHOC_WOULD_BLOCK; - } + socket->lastAttempt = CoreTiming::GetGlobalTimeUsScaled(); // Blocking Mode - else { + // Workaround: Forcing first attempt to be blocking to prevent issue related to lobby or high latency networks. (can be useful for GvG Next Plus, Dissidia 012, and Fate Unlimited Codes) + if (!flag || (g_Config.bForcedFirstConnect && socket->attemptCount == 1)) { // Simulate blocking behaviour with non-blocking socket u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id; - return WaitBlockingAdhocSocket(threadSocketId, PTP_CONNECT, id, nullptr, nullptr, (flag)? socket->send_timeout: timeout, nullptr, nullptr, "ptp connect"); + return WaitBlockingAdhocSocket(threadSocketId, PTP_CONNECT, id, nullptr, nullptr, (flag) ? std::max((int)socket->retry_interval, timeout) : timeout, nullptr, nullptr, "ptp connect"); + } + // NonBlocking Mode + else { + return ERROR_NET_ADHOC_WOULD_BLOCK; } } } @@ -3296,10 +3414,10 @@ static int sceNetAdhocPtpClose(int id, int unknown) { * @param flag Bitflags (Unused) * @return Socket ID > 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_ADDR, ADHOC_INVALID_PORT, ADHOC_SOCKET_ID_NOT_AVAIL, ADHOC_PORT_NOT_AVAIL, ADHOC_PORT_IN_USE, NET_NO_SPACE */ -static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int rexmt_int, int rexmt_cnt, int backlog, int unk) { - INFO_LOG(SCENET, "sceNetAdhocPtpListen(%s, %d, %d, %d, %d, %d, %d) at %08x", mac2str((SceNetEtherAddr*)srcmac).c_str(), sport,bufsize,rexmt_int,rexmt_cnt,backlog,unk, currentMIPS->pc); +static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int rexmt_int, int rexmt_cnt, int backlog, int flag) { + INFO_LOG(SCENET, "sceNetAdhocPtpListen(%s, %d, %d, %d, %d, %d, %d) at %08x", mac2str((SceNetEtherAddr*)srcmac).c_str(), sport,bufsize,rexmt_int,rexmt_cnt,backlog,flag, currentMIPS->pc); if (!g_Config.bEnableWlan) { - return 0; + return -1; } // Library is initialized SceNetEtherAddr * saddr = (SceNetEtherAddr *)srcmac; @@ -3310,6 +3428,10 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int } // Valid Address if (saddr != NULL && isLocalMAC(saddr)) { + // It's allowed to Listen and Open the same PTP port, But it's not allowed to Listen or Open the same PTP port twice. + if (isPTPPortInUse(sport, true)) + return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_IN_USE, "port in use"); + // Random Port required if (sport == 0) { //sport 0 should be shifted back to 0 when using offset Phantasy Star Portable 2 use this @@ -3325,9 +3447,8 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int // Valid Socket produced if (tcpsocket > 0) { // Change socket buffer size to be consistent on all platforms. - int ptpbufsize = std::max(bufsize, PSP_ADHOC_PTP_MSS); - setSockBufferSize(tcpsocket, SO_SNDBUF, ptpbufsize); - setSockBufferSize(tcpsocket, SO_RCVBUF, ptpbufsize*10); + setSockBufferSize(tcpsocket, SO_SNDBUF, bufsize*2); //PSP_ADHOC_PTP_MFS + setSockBufferSize(tcpsocket, SO_RCVBUF, bufsize*10); //PSP_ADHOC_PTP_MFS*10 // Enable KeepAlive setSockKeepAlive(tcpsocket, true, rexmt_int / 1000000L, rexmt_cnt); @@ -3380,8 +3501,10 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int // Socket Type internal->type = SOCK_PTP; - internal->recv_timeout = rexmt_int; + internal->retry_interval = rexmt_int; internal->retry_count = rexmt_cnt; + internal->nonblocking = flag; + internal->buffer_size = bufsize; // Copy Infrastructure Socket ID internal->data.ptp.id = tcpsocket; @@ -3404,7 +3527,7 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int changeBlockingMode(tcpsocket, 1); // Return PTP Socket Pointer - return i + 1; + return hleLogDebug(SCENET, i + 1, "success"); } // Free Memory @@ -3426,23 +3549,23 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int closesocket(tcpsocket); // Port not available (exclusively in use?) - return ERROR_NET_ADHOC_INVALID_PORT; //ERROR_NET_ADHOC_PORT_IN_USE; // ERROR_NET_ADHOC_PORT_NOT_AVAIL; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_NOT_AVAIL, "port not available"); //ERROR_NET_ADHOC_PORT_IN_USE; // ERROR_NET_ADHOC_INVALID_PORT; } // Socket not available - return ERROR_NET_ADHOC_SOCKET_ID_NOT_AVAIL; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_SOCKET_ID_NOT_AVAIL, "socket id not available"); } // Invalid Arguments - return ERROR_NET_ADHOC_INVALID_ARG; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg"); } // Invalid Addresses - return ERROR_NET_ADHOC_INVALID_ADDR; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ADDR, "invalid address"); } // Library is uninitialized - return ERROR_NET_ADHOC_NOT_INITIALIZED; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "adhoc not initialized"); } /** @@ -3468,6 +3591,7 @@ static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // Cast Socket auto socket = adhocSockets[id - 1]; auto& ptpsocket = socket->data.ptp; + socket->nonblocking = flag; // Connected Socket if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED) { @@ -3492,6 +3616,7 @@ static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // Success if (sent > 0) { + hleEatMicro(1000); // mostly 1ms, sometimes 1~10ms ? doesn't seems to be switching to a different thread during this duration // Save Length *len = sent; @@ -3502,7 +3627,7 @@ static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeou } // Non-Critical Error - else if (sent == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK || error == ETIMEDOUT)) { + else if (sent == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK)) { // Non-Blocking if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK; @@ -3560,6 +3685,7 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // Cast Socket auto socket = adhocSockets[id - 1]; auto& ptpsocket = socket->data.ptp; + socket->nonblocking = flag; if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED) { // Schedule Timeout Removal @@ -3593,6 +3719,8 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // Free Network Lock // _freeNetworkLock(); + hleEatMicro(1000); + // Received Data if (received > 0) { // Save Length @@ -3681,6 +3809,7 @@ static int sceNetAdhocPtpFlush(int id, int timeout, int nonblock) { // Cast Socket auto socket = adhocSockets[id - 1]; auto& ptpsocket = socket->data.ptp; + socket->nonblocking = nonblock; // Connected Socket if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED) { @@ -3736,12 +3865,17 @@ static int sceNetAdhocGameModeCreateMaster(u32 dataAddr, int size) { SceNetEtherAddr localMac; getLocalMac(&localMac); + u8* buf = (u8*)realloc(gameModeBuffer, size); + if (buf) + gameModeBuffer = buf; + u8* data = (u8*)malloc(size); if (data) { Memory::Memcpy(data, dataAddr, size); - gameModeSocket = sceNetAdhocPdpCreate((const char*)&localMac, ADHOC_GAMEMODE_PORT, GAMEMODE_BUFFER_SIZE, 0); masterGameModeArea = { 0, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 1, localMac, data }; - StartGameModeScheduler(); + // Socket's buffer size should fit the largest size from master/replicas, should we waited until master & all replicas to be created first before creating the socket? (ie. the first time UpdateMaster being called?) + gameModeSocket = sceNetAdhocPdpCreate((const char*)&localMac, ADHOC_GAMEMODE_PORT, size, 0); + StartGameModeScheduler(size); } return 0; // returned an id just like CreateReplica? always return 0? @@ -3786,7 +3920,7 @@ static int sceNetAdhocGameModeCreateReplica(const char *mac, u32 dataAddr, int s u8* data = (u8*)malloc(size); if (data) { Memory::Memcpy(data, dataAddr, size); - //int sock = sceNetAdhocPdpCreate(mac, ADHOC_GAMEMODE_PORT, GAMEMODE_BUFFER_SIZE, 0); + //int sock = sceNetAdhocPdpCreate(mac, ADHOC_GAMEMODE_PORT, size, 0); GameModeArea gma = { maxid + 1, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 0, *(SceNetEtherAddr*)mac, data }; replicaGameModeAreas.push_back(gma); ret = gma.id; @@ -3795,7 +3929,7 @@ static int sceNetAdhocGameModeCreateReplica(const char *mac, u32 dataAddr, int s } static int sceNetAdhocGameModeUpdateMaster() { - DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocGameModeUpdateMaster()"); + DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocGameModeUpdateMaster() at %08x", currentMIPS->pc); if (!netAdhocctlInited) return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized"); @@ -3834,7 +3968,7 @@ static int sceNetAdhocGameModeDeleteMaster() { } static int sceNetAdhocGameModeUpdateReplica(int id, u32 infoAddr) { - DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocGameModeUpdateReplica(%i, %08x)", id, infoAddr); + DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocGameModeUpdateReplica(%i, %08x) at %08x", id, infoAddr, currentMIPS->pc); if (!netAdhocctlInited) return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized"); @@ -3856,15 +3990,16 @@ static int sceNetAdhocGameModeUpdateReplica(int id, u32 infoAddr) { if (gma.id == id) { if (Memory::IsValidAddress(infoAddr)) { GameModeUpdateInfo* gmuinfo = (GameModeUpdateInfo*)Memory::GetPointer(infoAddr); + gmuinfo->length = sizeof(GameModeUpdateInfo); if (gma.data && gma.dataUpdated) { memcpy(Memory::GetPointer(gma.addr), gma.data, gma.size); gma.dataUpdated = 0; gmuinfo->updated = 1; + gmuinfo->timeStamp = std::max(gma.updateTimestamp, CoreTiming::GetGlobalTimeUsScaled() - 1000); } else { gmuinfo->updated = 0; } - gmuinfo->timeStamp = CoreTiming::GetGlobalTimeUsScaled(); } break; } @@ -3899,17 +4034,17 @@ static int sceNetAdhocGameModeDeleteReplica(int id) { } int sceNetAdhocGetSocketAlert(int id, u32 flagPtr) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocGetSocketAlert(%i, %08x) at %08x", id, flagPtr, currentMIPS->pc); + WARN_LOG_REPORT_ONCE(sceNetAdhocGetSocketAlert, SCENET, "UNTESTED sceNetAdhocGetSocketAlert(%i, %08x) at %08x", id, flagPtr, currentMIPS->pc); if (!Memory::IsValidAddress(flagPtr)) - return ERROR_NET_ADHOC_INVALID_ARG; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg"); if (id < 1 || id > MAX_SOCKET || adhocSockets[id - 1] == NULL) - return ERROR_NET_ADHOC_INVALID_SOCKET_ID; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_INVALID_SOCKET_ID, "invalid socket id"); s32_le flg = adhocSockets[id - 1]->flags; Memory::Write_U32(flg, flagPtr); - return 0; + return hleLogDebug(SCENET, 0, "flags = %08x", flg); } int NetAdhocMatching_Stop(int matchingId) { @@ -3929,15 +4064,11 @@ int NetAdhocMatching_Stop(int matchingId) { item->eventThread.join(); } - // Stop fake PSP Thread - if (matchingThreads[item->matching_thid] > 0) { + // Stop fake PSP Thread. + // kernelObjects may already been cleared early during a Shutdown, thus trying to access it may generates Warning/Error in the log + if (matchingThreads[item->matching_thid] > 0 && strcmp(__KernelGetThreadName(matchingThreads[item->matching_thid]), "ERROR") != 0) { __KernelStopThread(matchingThreads[item->matching_thid], SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching stopped"); __KernelDeleteThread(matchingThreads[item->matching_thid], SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching deleted"); - /*item->matchingThread->Terminate(); - if (item->matchingThread && item->matchingThread->Stopped()) { - delete item->matchingThread; - item->matchingThread = nullptr; - }*/ } matchingThreads[item->matching_thid] = 0; @@ -4155,7 +4286,7 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle if (keepalive_int < 1) context->keepalive_int = PSP_ADHOCCTL_PING_TIMEOUT; else context->keepalive_int = keepalive_int; // client might set this to 0 context->keepalivecounter = init_count; // used to multiply keepalive_int as timeout context->timeout = (((u64)(keepalive_int) + (u64)rexmt_int) * (u64)init_count); - context->timeout += (adhocDefaultTimeout * 1000ULL); // For internet play we need higher timeout than what the game wanted + context->timeout += adhocDefaultTimeout; // For internet play we need higher timeout than what the game wanted context->handler = handler; // Fill in Selfpeer @@ -4282,10 +4413,7 @@ int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int peerlock.unlock(); // Give a little time to make sure matching Threads are ready before the game use the next sceNet functions, should've checked for status instead of guessing the time? - //sleep_ms(adhocMatchingEventDelayMS); - hleDelayResult(0, "give some time", adhocMatchingEventDelayMS * 1000); - - return 0; + return hleDelayResult(0, "give some time", adhocMatchingEventDelay); } #define KERNEL_PARTITION_ID 1 @@ -4587,83 +4715,71 @@ int sceNetAdhocMatchingSetHelloOpt(int matchingId, int optLenAddr, u32 optDataAd if (!g_Config.bEnableWlan) return -1; - if (!netAdhocMatchingInited) - return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED; - + if (!netAdhocMatchingInited) + return hleLogDebug(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhocmatching not initialized"); + // Multithreading Lock peerlock.lock(); - SceNetAdhocMatchingContext * context = findMatchingContext(matchingId); - + SceNetAdhocMatchingContext* context = findMatchingContext(matchingId); + // Multithreading Unlock peerlock.unlock(); - // Found Context - if (context != NULL) + // Context not found + if (context == NULL) + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ID, "adhocmatching invalid id"); + + // Invalid Matching Mode (Child) + if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD) + return hleLogDebug(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_MODE, "adhocmatching invalid mode"); + + // Context not running + if (!context->running) + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_RUNNING, "adhocmatching not running"); + + // Invalid Optional Data Length + if ((optLenAddr != 0) && (optDataAddr == 0)) + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN, "adhocmatching invalid optlen"); //ERROR_NET_ADHOC_MATCHING_INVALID_ARG + + // Grab Existing Hello Data + void* hello = context->hello; + + // Free Previous Hello Data, or Reuse it + //free(hello); + + // Allocation Required + if (optLenAddr > 0) { - // Valid Matching Modes - if (context->mode != PSP_ADHOC_MATCHING_MODE_CHILD) - { - // Running Context - if (context->running) - { - // Valid Optional Data Length - if ((optLenAddr == 0) || (optLenAddr > 0 && optDataAddr != 0)) - { - // Grab Existing Hello Data - void * hello = context->hello; - - // Free Previous Hello Data, or Reuse it - //free(hello); - - // Allocation Required - if (optLenAddr > 0) - { - // Allocate Memory - if (optLenAddr > context->hellolen) { - hello = realloc(hello, optLenAddr); - } - - // Out of Memory - if (hello == NULL) { - context->hellolen = 0; - return ERROR_NET_ADHOC_MATCHING_NO_SPACE; - } - - // Clone Hello Data - //memcpy(hello, opt, optLenAddr); - Memory::Memcpy(hello, optDataAddr, optLenAddr); - - // Set Hello Data - context->hello = (uint8_t*)hello; - context->hellolen = optLenAddr; - context->helloAddr = optDataAddr; - } - else - { - // Delete Hello Data - context->hellolen = 0; - context->helloAddr = 0; - //free(context->hello); // Doesn't need to free it since it will be reused later - //context->hello = NULL; - } - - // Return Success - return 0; - } - - // Invalid Optional Data Length - return ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN; - } - - // Context not running - return ERROR_NET_ADHOC_MATCHING_NOT_RUNNING; + // Allocate Memory + if (optLenAddr > context->hellolen) { + hello = realloc(hello, optLenAddr); } - // Invalid Matching Mode (Child) - return ERROR_NET_ADHOC_MATCHING_INVALID_MODE; + // Out of Memory + if (hello == NULL) { + context->hellolen = 0; + return ERROR_NET_ADHOC_MATCHING_NO_SPACE; + } + + // Clone Hello Data + Memory::Memcpy(hello, optDataAddr, optLenAddr); + + // Set Hello Data + context->hello = (uint8_t*)hello; + context->hellolen = optLenAddr; + context->helloAddr = optDataAddr; + } + else + { + // Delete Hello Data + context->hellolen = 0; + context->helloAddr = 0; + //free(context->hello); // Doesn't need to free it since it will be reused later + //context->hello = NULL; } + // Return Success return 0; } @@ -4673,209 +4789,200 @@ static int sceNetAdhocMatchingGetMembers(int matchingId, u32 sizeAddr, u32 buf) return -1; if (!netAdhocMatchingInited) - return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhocmatching not initialized"); // Minimum Argument - if (!Memory::IsValidAddress(sizeAddr)) + if (!Memory::IsValidAddress(sizeAddr)) return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ARG, "adhocmatching invalid arg"); // Multithreading Lock peerlock.lock(); // Find Matching Context - SceNetAdhocMatchingContext * context = findMatchingContext(matchingId); + SceNetAdhocMatchingContext* context = findMatchingContext(matchingId); // Multithreading Unlock peerlock.unlock(); - // Found Context - if (context != NULL) - { - // Running Context - if (context->running) - { - // Length Buffer available - if (sizeAddr != 0) - { - int * buflen = (int *)Memory::GetPointer(sizeAddr); - SceNetAdhocMatchingMemberInfoEmu * buf2 = NULL; - if (Memory::IsValidAddress(buf)) { - buf2 = (SceNetAdhocMatchingMemberInfoEmu *)Memory::GetPointer(buf); - } + // Context not found + if (context == NULL) + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ID, "adhocmatching invalid id"); - // Number of Connected Peers, should we exclude timeout members? - bool excludeTimedout = false; // false; - uint32_t peercount = countConnectedPeers(context, excludeTimedout); - - // Calculate Connected Peer Bytesize - int available = sizeof(SceNetAdhocMatchingMemberInfoEmu) * peercount; - - // Length Returner Mode - if (buf == 0) - { - // Get Connected Peer Count - *buflen = available; - DEBUG_LOG(SCENET, "MemberList [Connected: %i]", peercount); - } - - // Normal Mode - else - { - // Fix Negative Length - if ((*buflen) < 0) *buflen = 0; - - // Fix Oversize Request - if ((*buflen) > available) *buflen = available; - - // Clear Memory - memset(buf2, 0, *buflen); - - // Calculate Requested Peer Count - int requestedpeers = (*buflen) / sizeof(SceNetAdhocMatchingMemberInfoEmu); - - // Filled Request Counter - int filledpeers = 0; - - if (requestedpeers > 0) - { - // Add Self-Peer first, unless if there is existing Parent/P2P peer - if (peercount == 1 || context->mode != PSP_ADHOC_MATCHING_MODE_CHILD) { - // Add Local MAC - buf2[filledpeers++].mac_addr = context->mac; - - DEBUG_LOG(SCENET, "MemberSelf [%s]", mac2str(&context->mac).c_str()); - } - - // Room for more than local peer - if (requestedpeers > 1) - { - // P2P Mode - if (context->mode == PSP_ADHOC_MATCHING_MODE_P2P) - { - // Find P2P Brother - SceNetAdhocMatchingMemberInternal * p2p = findP2P(context, excludeTimedout); - - // P2P Brother found - if (p2p != NULL) - { - // Faking lastping - auto friendpeer = findFriend(&p2p->mac); - if (p2p->lastping != 0 && friendpeer != NULL && friendpeer->last_recv != 0) - p2p->lastping = CoreTiming::GetGlobalTimeUsScaled() - 1; - else - p2p->lastping = 0; - - // Add P2P Brother MAC - buf2[filledpeers++].mac_addr = p2p->mac; - - DEBUG_LOG(SCENET, "MemberP2P [%s]", mac2str(&p2p->mac).c_str()); - } - } - - // Parent or Child Mode - else - { - // Add Parent first - SceNetAdhocMatchingMemberInternal* parentpeer = findParent(context); - if (parentpeer != NULL) { - // Faking lastping - auto friendpeer = findFriend(&parentpeer->mac); - if (parentpeer->lastping != 0 && friendpeer != NULL && friendpeer->last_recv != 0) - parentpeer->lastping = CoreTiming::GetGlobalTimeUsScaled() - 1; - else - parentpeer->lastping = 0; - - // Add Parent MAC - buf2[filledpeers++].mac_addr = parentpeer->mac; - - DEBUG_LOG(SCENET, "MemberParent [%s]", mac2str(&parentpeer->mac).c_str()); - } - - // We may need to rearrange children where last joined player placed last - std::deque sortedPeers; - - // Iterate Peer List - SceNetAdhocMatchingMemberInternal * peer = context->peerlist; - for (; peer != NULL && filledpeers < requestedpeers; peer = peer->next) - { - // Should we exclude timedout members? - if (!excludeTimedout || peer->lastping != 0) { - // Faking lastping - auto friendpeer = findFriend(&peer->mac); - if (peer->lastping != 0 && friendpeer != NULL && friendpeer->last_recv != 0) - peer->lastping = CoreTiming::GetGlobalTimeUsScaled() - 1; - else - peer->lastping = 0; - - // Add Peer MAC - sortedPeers.push_front(peer); - } - } - - // Iterate rearranged peers - for (const auto& peer : sortedPeers) { - // Parent Mode - if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT) { - // Interested in Children - if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) { - // Add Child MAC - buf2[filledpeers++].mac_addr = peer->mac; - - DEBUG_LOG(SCENET, "MemberChild [%s]", mac2str(&peer->mac).c_str()); - } - } - - // Child Mode - else { - // Interested in Siblings - if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) { - // Add Peer MAC - buf2[filledpeers++].mac_addr = peer->mac; - - DEBUG_LOG(SCENET, "MemberSibling [%s]", mac2str(&peer->mac).c_str()); - } - // Self Peer - else if (peer->state == 0) { - // Add Local MAC - buf2[filledpeers++].mac_addr = peer->mac; - - DEBUG_LOG(SCENET, "MemberSelf [%s]", mac2str(&peer->mac).c_str()); - } - - } - } - sortedPeers.clear(); - } - } - - // Link Result List - for (int i = 0; i < filledpeers - 1; i++) - { - // Link Next Element - //buf2[i].next = &buf2[i + 1]; - buf2[i].next = buf + (sizeof(SceNetAdhocMatchingMemberInfoEmu)*(i+1LL)); - } - // Fix Last Element - if (filledpeers > 0) buf2[filledpeers - 1].next = 0; - } - - // Fix Buffer Size - *buflen = sizeof(SceNetAdhocMatchingMemberInfoEmu) * filledpeers; - DEBUG_LOG(SCENET, "MemberList [Requested: %i][Discovered: %i]", requestedpeers, filledpeers); - } - - // Return Success - return 0; - } - - // Invalid Arguments - return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ARG, "adhocmatching invalid arg"); - } - - // Context not running + // Context not running + if (!context->running) return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_RUNNING, "adhocmatching not running"); + + // Buffer Length not available + if (!Memory::IsValidAddress(sizeAddr)) + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ARG, "adhocmatching invalid arg"); + + int* buflen = (int*)Memory::GetPointer(sizeAddr); + SceNetAdhocMatchingMemberInfoEmu* buf2 = NULL; + if (Memory::IsValidAddress(buf)) { + buf2 = (SceNetAdhocMatchingMemberInfoEmu*)Memory::GetPointer(buf); } - // Invalid Matching ID - return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ID, "adhocmatching invalid id"); + // Number of Connected Peers, should we exclude timeout members? + bool excludeTimedout = false; // false; + uint32_t peercount = countConnectedPeers(context, excludeTimedout); + + // Calculate Connected Peer Bytesize + int available = sizeof(SceNetAdhocMatchingMemberInfoEmu) * peercount; + + // Length Returner Mode + if (buf == 0) + { + // Get Connected Peer Count + *buflen = available; + DEBUG_LOG(SCENET, "MemberList [Connected: %i]", peercount); + } + + // Normal Mode + else + { + // Fix Negative Length + if ((*buflen) < 0) *buflen = 0; + + // Fix Oversize Request + if ((*buflen) > available) *buflen = available; + + // Clear Memory + memset(buf2, 0, *buflen); + + // Calculate Requested Peer Count + int requestedpeers = (*buflen) / sizeof(SceNetAdhocMatchingMemberInfoEmu); + + // Filled Request Counter + int filledpeers = 0; + + if (requestedpeers > 0) + { + // Add Self-Peer first, unless if there is existing Parent/P2P peer + if (peercount == 1 || context->mode != PSP_ADHOC_MATCHING_MODE_CHILD) { + // Add Local MAC + buf2[filledpeers++].mac_addr = context->mac; + + DEBUG_LOG(SCENET, "MemberSelf [%s]", mac2str(&context->mac).c_str()); + } + + // Room for more than local peer + if (requestedpeers > 1) + { + // P2P Mode + if (context->mode == PSP_ADHOC_MATCHING_MODE_P2P) + { + // Find P2P Brother + SceNetAdhocMatchingMemberInternal* p2p = findP2P(context, excludeTimedout); + + // P2P Brother found + if (p2p != NULL) + { + // Faking lastping + auto friendpeer = findFriend(&p2p->mac); + if (p2p->lastping != 0 && friendpeer != NULL && friendpeer->last_recv != 0) + p2p->lastping = std::max(p2p->lastping, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta); + else + p2p->lastping = 0; + + // Add P2P Brother MAC + buf2[filledpeers++].mac_addr = p2p->mac; + + DEBUG_LOG(SCENET, "MemberP2P [%s]", mac2str(&p2p->mac).c_str()); + } + } + + // Parent or Child Mode + else + { + // Add Parent first + SceNetAdhocMatchingMemberInternal* parentpeer = findParent(context); + if (parentpeer != NULL) { + // Faking lastping + auto friendpeer = findFriend(&parentpeer->mac); + if (parentpeer->lastping != 0 && friendpeer != NULL && friendpeer->last_recv != 0) + parentpeer->lastping = std::max(parentpeer->lastping, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta); + else + parentpeer->lastping = 0; + + // Add Parent MAC + buf2[filledpeers++].mac_addr = parentpeer->mac; + + DEBUG_LOG(SCENET, "MemberParent [%s]", mac2str(&parentpeer->mac).c_str()); + } + + // We may need to rearrange children where last joined player placed last + std::deque sortedPeers; + + // Iterate Peer List + SceNetAdhocMatchingMemberInternal* peer = context->peerlist; + for (; peer != NULL && filledpeers < requestedpeers; peer = peer->next) + { + // Should we exclude timedout members? + if (!excludeTimedout || peer->lastping != 0) { + // Faking lastping + auto friendpeer = findFriend(&peer->mac); + if (peer->lastping != 0 && friendpeer != NULL && friendpeer->last_recv != 0) + peer->lastping = std::max(peer->lastping, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta); + else + peer->lastping = 0; + + // Add Peer MAC + sortedPeers.push_front(peer); + } + } + + // Iterate rearranged peers + for (const auto& peer : sortedPeers) { + // Parent Mode + if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT) { + // Interested in Children + if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) { + // Add Child MAC + buf2[filledpeers++].mac_addr = peer->mac; + + DEBUG_LOG(SCENET, "MemberChild [%s]", mac2str(&peer->mac).c_str()); + } + } + + // Child Mode + else { + // Interested in Siblings + if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) { + // Add Peer MAC + buf2[filledpeers++].mac_addr = peer->mac; + + DEBUG_LOG(SCENET, "MemberSibling [%s]", mac2str(&peer->mac).c_str()); + } + // Self Peer + else if (peer->state == 0) { + // Add Local MAC + buf2[filledpeers++].mac_addr = peer->mac; + + DEBUG_LOG(SCENET, "MemberSelf [%s]", mac2str(&peer->mac).c_str()); + } + + } + } + sortedPeers.clear(); + } + } + + // Link Result List + for (int i = 0; i < filledpeers - 1; i++) + { + // Link Next Element + //buf2[i].next = &buf2[i + 1]; + buf2[i].next = buf + (sizeof(SceNetAdhocMatchingMemberInfoEmu) * (i + 1LL)); + } + // Fix Last Element + if (filledpeers > 0) buf2[filledpeers - 1].next = 0; + } + + // Fix Buffer Size + *buflen = sizeof(SceNetAdhocMatchingMemberInfoEmu) * filledpeers; + DEBUG_LOG(SCENET, "MemberList [Requested: %i][Discovered: %i]", requestedpeers, filledpeers); + } + + // Return Success + return hleDelayResult(0, "delay 1 ~ 10ms", 1000); // seems to have different thread running within the delay duration } // Gran Turismo may replace the 1st bit of the 1st byte of MAC address's OUI with 0 (unicast bit), or replace the whole 6-bytes of MAC address with all 00 (invalid mac) for unknown reason @@ -5092,11 +5199,13 @@ int sceNetAdhocMatchingGetPoolStat(u32 poolstatPtr) { void __NetTriggerCallbacks() { std::lock_guard adhocGuard(adhocEvtMtx); + hleSkipDeadbeef(); int delayus = 10000; - + auto params = adhocctlEvents.begin(); if (params != adhocctlEvents.end()) { + int newState = adhocctlState; u32 flags = params->first; u32 error = params->second; u32_le args[3] = { 0, 0, 0 }; @@ -5104,7 +5213,7 @@ void __NetTriggerCallbacks() args[1] = error; // FIXME: When Joining a group, Do we need to wait for group creator's peer data before triggering the callback to make sure the game not to thinks we're the group creator? - u64 now = (u64)(time_now_d() * 1000.0); + u64 now = (u64)(time_now_d() * 1000000.0); if ((flags != ADHOCCTL_EVENT_CONNECT && flags != ADHOCCTL_EVENT_GAME) || adhocConnectionType != ADHOC_JOIN || getActivePeerCount() > 0 || now - adhocctlStartTime > adhocDefaultTimeout) { // Since 0 is a valid index to types_ we use -1 to detects if it was loaded from an old save state @@ -5112,30 +5221,42 @@ void __NetTriggerCallbacks() actionAfterAdhocMipsCall = __KernelRegisterActionType(AfterAdhocMipsCall::Create); } - delayus = (adhocEventPollDelayMS + 2 * adhocExtraPollDelayMS) * 1000; // Added an extra delay to prevent I/O Timing method from causing disconnection + delayus = adhocEventPollDelay; // May need to add an extra delay if a certain I/O Timing method causing disconnection issue switch (flags) { case ADHOCCTL_EVENT_CONNECT: - adhocctlState = ADHOCCTL_STATE_CONNECTED; - delayus = (adhocEventDelayMS + 2 * adhocExtraPollDelayMS) * 1000; // May affects Dissidia 012 and GTA VCS + newState = ADHOCCTL_STATE_CONNECTED; + if (adhocConnectionType != ADHOC_JOIN) + delayus = adhocEventDelay; // May affects Dissidia 012 and GTA VCS break; case ADHOCCTL_EVENT_SCAN: // notified only when scan completed? - adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + newState = ADHOCCTL_STATE_DISCONNECTED; break; case ADHOCCTL_EVENT_DISCONNECT: - adhocctlState = ADHOCCTL_STATE_DISCONNECTED; - break; - case ADHOCCTL_EVENT_GAME: - adhocctlState = ADHOCCTL_STATE_GAMEMODE; - delayus = (adhocEventDelayMS + 2 * adhocExtraPollDelayMS) * 1000; + newState = ADHOCCTL_STATE_DISCONNECTED; break; + case ADHOCCTL_EVENT_GAME: + { + newState = ADHOCCTL_STATE_GAMEMODE; + if (adhocConnectionType != ADHOC_JOIN) + delayus = adhocEventDelay; + // Shows player list + INFO_LOG(SCENET, "GameMode - All players have joined:"); + int i = 0; + for (auto& mac : gameModeMacs) { + INFO_LOG(SCENET, "GameMode macAddress#%d=%s", i++, mac2str(&mac).c_str()); + if (i >= ADHOCCTL_GAMEMODE_MAX_MEMBERS) + break; + } + } + break; case ADHOCCTL_EVENT_DISCOVER: - adhocctlState = ADHOCCTL_STATE_DISCOVER; + newState = ADHOCCTL_STATE_DISCOVER; break; case ADHOCCTL_EVENT_WOL_INTERRUPT: - adhocctlState = ADHOCCTL_STATE_WOL; + newState = ADHOCCTL_STATE_WOL; break; case ADHOCCTL_EVENT_ERROR: - adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + newState = ADHOCCTL_STATE_DISCONNECTED; break; } @@ -5147,12 +5268,14 @@ void __NetTriggerCallbacks() hleEnqueueCall(it->second.entryPoint, 3, args, after); } adhocctlEvents.pop_front(); + // Since we don't have beforeAction, simulate it using ScheduleEvent + ScheduleAdhocctlState(flags, newState, delayus, "adhocctl callback state"); + return; } } // Must be delayed long enough whenever there is a pending callback. Should it be 100-500ms for Adhocctl Events? or Not Less than the delays on sceNetAdhocctl HLE? sceKernelDelayThread(delayus); - hleSkipDeadbeef(); } void __NetMatchingCallbacks() //(int matchingId) @@ -5175,7 +5298,7 @@ void __NetMatchingCallbacks() //(int matchingId) after->SetData(args[0], args[1], args[2]); hleEnqueueCall(args[5], 5, args, after); matchingEvents.pop_front(); - delayus = (adhocMatchingEventDelayMS + 2 * adhocExtraPollDelayMS) * 1000; // Added an extra delay to prevent I/O Timing method from causing disconnection + delayus = (adhocMatchingEventDelay + adhocExtraDelay); // Added an extra delay to prevent I/O Timing method from causing disconnection } // Must be delayed long enough whenever there is a pending callback. Should it be 10-100ms for Matching Events? or Not Less than the delays on sceNetAdhocMatching HLE? @@ -5248,13 +5371,13 @@ int NetAdhocctl_ExitGameMode() { } static int sceNetAdhocctlExitGameMode() { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlExitGameMode()"); + WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlExitGameMode() at %08x", currentMIPS->pc); return NetAdhocctl_ExitGameMode(); } static int sceNetAdhocctlGetGameModeInfo(u32 infoAddr) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlGetGameModeInfo(%08x)", infoAddr); + DEBUG_LOG(SCENET, "sceNetAdhocctlGetGameModeInfo(%08x)", infoAddr); if (!netAdhocctlInited) return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized"); @@ -5266,7 +5389,7 @@ static int sceNetAdhocctlGetGameModeInfo(u32 infoAddr) { gmInfo->num = static_cast(gameModeMacs.size()); int i = 0; for (auto& mac : gameModeMacs) { - INFO_LOG(SCENET, "GameMode macAddress#%d=%s", i, mac2str(&mac).c_str()); + VERBOSE_LOG(SCENET, "GameMode macAddress#%d=%s", i, mac2str(&mac).c_str()); gmInfo->members[i++] = mac; if (i >= ADHOCCTL_GAMEMODE_MAX_MEMBERS) break; @@ -5322,7 +5445,7 @@ static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) { if (!excludeTimedout || peer->last_recv != 0) { // Faking Last Receive Time if (peer->last_recv != 0) - peer->last_recv = CoreTiming::GetGlobalTimeUsScaled() - 1; + peer->last_recv = std::max(peer->last_recv, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta); // Copy Peer Info buf[discovered].nickname = peer->nickname; @@ -5354,7 +5477,7 @@ static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) { peerlock.unlock(); // Return Success - return 0; + return hleDelayResult(0, "delay 1 ~ 10ms", 1000); // seems to have different thread running within the delay duration } // Invalid Arguments @@ -5373,7 +5496,7 @@ static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 b memcpy(nckName, nickName, ADHOCCTL_NICKNAME_LEN); // Copied to null-terminated var to prevent unexpected behaviour on Logs nckName[ADHOCCTL_NICKNAME_LEN - 1] = 0; - WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlGetAddrByName(%s, [%08x]=%d/%zu, %08x)", nckName, sizeAddr, buflen ? *buflen : -1, sizeof(SceNetAdhocctlPeerInfoEmu), bufAddr); + WARN_LOG_REPORT_ONCE(sceNetAdhocctlGetAddrByName, SCENET, "UNTESTED sceNetAdhocctlGetAddrByName(%s, [%08x]=%d/%zu, %08x) at %08x", nckName, sizeAddr, buflen ? *buflen : -1, sizeof(SceNetAdhocctlPeerInfoEmu), bufAddr, currentMIPS->pc); // Library initialized if (netAdhocctlInited) @@ -5388,8 +5511,11 @@ static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 b peerlock.lock(); // Length Calculation Mode - if (buf == NULL) *buflen = getNicknameCount(nickName) * sizeof(SceNetAdhocctlPeerInfoEmu); - + if (buf == NULL) { + int foundName = getNicknameCount(nickName); + *buflen = foundName * sizeof(SceNetAdhocctlPeerInfoEmu); + DEBUG_LOG(SCENET, "PeerNameList [%s: %i]", nickName, foundName); + } // Normal Information Mode else { @@ -5410,13 +5536,18 @@ static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 b { // Get Local IP Address sockaddr_in addr; + SceNetEtherAddr mac; getLocalIp(&addr); buf[discovered].nickname = parameter.nickname; buf[discovered].nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char - getLocalMac(&buf[discovered].mac_addr); + getLocalMac(&mac); + buf[discovered].mac_addr = mac; buf[discovered].flags = 0x0400; - buf[discovered++].last_recv = CoreTiming::GetGlobalTimeUsScaled() - 1; + u64 lastrecv = CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta; + buf[discovered++].last_recv = lastrecv; + + DEBUG_LOG(SCENET, "Peer [%s][%s][%s][%llu]", mac2str(&mac).c_str(), inet_ntoa(addr.sin_addr), nickName, lastrecv); } // Peer Reference @@ -5426,11 +5557,10 @@ static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 b for (; peer != NULL && discovered < requestcount; peer = peer->next) { // Match found - if (strncmp((char *)&peer->nickname.data, nickName, ADHOCCTL_NICKNAME_LEN) == 0) + if (peer->last_recv != 0 && strncmp((char *)&peer->nickname.data, nickName, ADHOCCTL_NICKNAME_LEN) == 0) { // Fake Receive Time - if (peer->last_recv != 0) - peer->last_recv = CoreTiming::GetGlobalTimeUsScaled() - 1; + peer->last_recv = std::max(peer->last_recv, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta); // Copy Peer Info buf[discovered].nickname = peer->nickname; @@ -5438,6 +5568,9 @@ static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 b buf[discovered].mac_addr = peer->mac_addr; buf[discovered].flags = 0x0400; buf[discovered++].last_recv = peer->last_recv; + + u32_le ipaddr = peer->ip_addr; + DEBUG_LOG(SCENET, "Peer [%s][%s][%s][%llu]", mac2str(&peer->mac_addr).c_str(), inet_ntoa(*(in_addr*)&ipaddr), (const char*)&peer->nickname.data, peer->last_recv); } } @@ -5454,13 +5587,14 @@ static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 b // Fix Buffer Size *buflen = discovered * sizeof(SceNetAdhocctlPeerInfoEmu); + DEBUG_LOG(SCENET, "PeerNameList [%s][Requested: %i][Discovered: %i]", nickName, requestcount, discovered); } // Multithreading Unlock peerlock.unlock(); // Return Success - return 0; + return hleLogDebug(SCENET, hleDelayResult(0, "delay 1 ~ 10ms", 1000), "success"); // FIXME: Might have similar delay with GetPeerList? need to know which games using this tho } // Invalid Arguments @@ -6803,7 +6937,7 @@ int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is usi now = CoreTiming::GetGlobalTimeUsScaled(); u64_le delta = now - peer->last_recv; DEBUG_LOG(SCENET, "Timestamp Delta: %llu (%llu - %llu) from %s", delta, now, peer->last_recv, mac2str(&sendermac).c_str()); - if (/*context->rxbuf[0] > 0 &&*/ peer->last_recv != 0) peer->last_recv = now - 1; // - context->keepalive_int; // May need to deduce by ping interval to prevent Dissidia 012 unable to see other players (ie. disappearing issue) + if (peer->last_recv != 0) peer->last_recv = now - defaultLastRecvDelta; } else { WARN_LOG(SCENET, "InputLoop[%d]: Unknown Peer[%s:%u] (Recved=%i, Length=%i)", matchingId, mac2str(&sendermac).c_str(), senderport, recvresult, rxbuflen); diff --git a/Core/HLE/sceNetAdhoc.h b/Core/HLE/sceNetAdhoc.h index 0ec9218bb5..682a609b2f 100644 --- a/Core/HLE/sceNetAdhoc.h +++ b/Core/HLE/sceNetAdhoc.h @@ -98,11 +98,12 @@ int NetAdhoc_Term(); extern bool netAdhocInited; extern bool netAdhocctlInited; extern bool networkInited; -extern int adhocDefaultTimeout; -extern int adhocExtraPollDelayMS; -extern int adhocEventPollDelayMS; -extern int adhocMatchingEventDelayMS; -extern int adhocEventDelayMS; // This will affect the duration of "Connecting..." dialog/message box in .Hack//Link and Naruto Ultimate Ninja Heroes 3 +extern int adhocDefaultTimeout; //3000000 usec +extern int adhocDefaultDelay; //10000 +extern int adhocExtraDelay; //20000 +extern int adhocEventPollDelay; //100000; // Seems to be the same with PSP_ADHOCCTL_RECV_TIMEOUT +extern int adhocMatchingEventDelay; //30000 +extern int adhocEventDelay; //1000000 extern std::recursive_mutex adhocEvtMtx; extern int IsAdhocctlInCB; diff --git a/Core/HW/MemoryStick.cpp b/Core/HW/MemoryStick.cpp index f0d9a38708..da0058261f 100644 --- a/Core/HW/MemoryStick.cpp +++ b/Core/HW/MemoryStick.cpp @@ -71,15 +71,18 @@ u64 MemoryStick_SectorSize() { } u64 MemoryStick_FreeSpace() { - u64 freeSpace = pspFileSystem.FreeSpace("ms0:/"); - // Cap the memory stick size to avoid math errors when old games get sizes that were // hard to imagine back then. // We have a compat setting to make it even smaller for Harry Potter : Goblet of Fire, see #13266. - const u64 memStickSize = PSP_CoreParameter().compat.flags().ReportSmallMemstick ? smallMemstickSize : normalMemstickSize; - if (freeSpace < memStickSize) - return freeSpace; - return memStickSize; + const u64 memStickSize = PSP_CoreParameter().compat.flags().ReportSmallMemstick ? smallMemstickSize : (u64)g_Config.iMemStickSizeGB * 1024 * 1024 * 1024; + u64 usedSpace = pspFileSystem.getDirSize("ms0:/PSP/SAVEDATA/");// Assume the memory stick is only used to store savedata. + u64 freeSpace; + if (usedSpace >= memStickSize) + freeSpace = 0; + else + freeSpace = memStickSize - usedSpace; + + return freeSpace; } void MemoryStick_SetFatState(MemStickFatState state) { diff --git a/Core/MIPS/MIPSVFPUUtils.h b/Core/MIPS/MIPSVFPUUtils.h index cf7c1e7426..e43c588a41 100644 --- a/Core/MIPS/MIPSVFPUUtils.h +++ b/Core/MIPS/MIPSVFPUUtils.h @@ -29,67 +29,48 @@ inline int Xpose(int v) { return v^0x20; } +// Half of PI, or 90 degrees. #ifndef M_PI_2 #define M_PI_2 1.57079632679489661923 #endif -// Some games depend on exact values, but sinf() and cosf() aren't always precise. +// The VFPU uses weird angles where 4.0 represents a full circle. This makes it possible to return +// exact 1.0/-1.0 values at certain angles. We get close enough for #2921 and #12900 by computing +// things in double precision, multiplying the input by pi/2. +// +// A better solution would be to tailor some sine approximation for the 0..90 degrees range, compute +// modulo manually and mirror that around the circle. Also correctly special casing for inf/nan inputs +// and just trying to match it as closely as possible to the real PSP. +// // Stepping down to [0, 2pi) helps, but we also check common exact-result values. // TODO: cos(1) and sin(2) should be -0.0, but doing that gives wrong results (possibly from floorf.) +// Messing around with the modulo functions? try https://www.desmos.com/calculator. + inline float vfpu_sin(float angle) { - angle -= floorf(angle * 0.25f) * 4.f; - if (angle == 0.0f || angle == 2.0f) { - return 0.0f; - } else if (angle == 1.0f) { - return 1.0f; - } else if (angle == 3.0f) { - return -1.0f; - } - angle *= (float)M_PI_2; - return sinf(angle); + return (float)sin((double)angle * M_PI_2); } inline float vfpu_cos(float angle) { - angle -= floorf(angle * 0.25f) * 4.f; - if (angle == 1.0f || angle == 3.0f) { - return 0.0f; - } else if (angle == 0.0f) { - return 1.0f; - } else if (angle == 2.0f) { - return -1.0f; - } - angle *= (float)M_PI_2; - return cosf(angle); + return (float)cos((double)angle * M_PI_2); } inline float vfpu_asin(float angle) { return asinf(angle) / M_PI_2; } -inline void vfpu_sincos(float angle, float &sine, float &cosine) { - angle -= floorf(angle * 0.25f) * 4.f; - if (angle == 0.0f) { - sine = 0.0f; - cosine = 1.0f; - } else if (angle == 1.0f) { - sine = 1.0f; - cosine = 0.0f; - } else if (angle == 2.0f) { - sine = 0.0f; - cosine = -1.0f; - } else if (angle == 3.0f) { - sine = -1.0f; - cosine = 0.0f; - } else { - angle *= (float)M_PI_2; +inline void vfpu_sincos(float angle_f, float &sine, float &cosine) { + double angle = (double)angle_f * M_PI_2; #if defined(__linux__) - sincosf(angle, &sine, &cosine); + double d_sine; + double d_cosine; + sincos(angle, &d_sine, &d_cosine); + sine = (float)d_sine; + cosine = (float)d_cosine; #else - sine = sinf(angle); - cosine = cosf(angle); + sine = (float)sin(angle); + cosine = (float)cos(angle); #endif - } } inline float vfpu_clamp(float v, float min, float max) { diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index be0248dff7..a5870f87c3 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -60,7 +60,9 @@ namespace Reporting // Temporarily stores a reference to the hostname. static std::string lastHostname; // Keeps track of report-only-once identifiers. Since they're always constants, a pointer is okay. - static std::map logOnceUsed; + static std::map logNTimes; + static std::mutex logNTimesLock; + // Keeps track of whether a harmful setting was ever used. static bool everUnsupported = false; // Support is cached here to avoid checking it on every single request. @@ -303,7 +305,7 @@ namespace Reporting { // New game, clean slate. spamProtectionCount = 0; - logOnceUsed.clear(); + logNTimes.clear(); everUnsupported = false; currentSupported = IsSupported(); pendingMessagesDone = false; @@ -347,9 +349,10 @@ namespace Reporting bool ShouldLogNTimes(const char *identifier, int count) { // True if it wasn't there already -> so yes, log. - auto iter = logOnceUsed.find(identifier); - if (iter == logOnceUsed.end()) { - logOnceUsed.insert(std::pair(identifier, 1)); + std::lock_guard lock(logNTimesLock); + auto iter = logNTimes.find(identifier); + if (iter == logNTimes.end()) { + logNTimes.insert(std::pair(identifier, 1)); return true; } else { if (iter->second >= count) { @@ -361,6 +364,11 @@ namespace Reporting } } + void ResetCounts() { + std::lock_guard lock(logNTimesLock); + logNTimes.clear(); + } + std::string CurrentGameID() { // TODO: Maybe ParamSFOData shouldn't include nulls in std::strings? Don't work to break savedata, though... diff --git a/Core/Reporting.h b/Core/Reporting.h index 1d168cb88c..50b24f93c8 100644 --- a/Core/Reporting.h +++ b/Core/Reporting.h @@ -53,6 +53,9 @@ namespace Reporting void Init(); void Shutdown(); + // Resets counts on any count-limited logs (see ShouldLogNTimes). + void ResetCounts(); + // Check savestate compatibility, mostly needed on load. void DoState(PointerWrap &p); diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 892297c7b8..4353f77c0d 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1717,7 +1717,7 @@ void FramebufferManagerCommon::SetRenderSize(VirtualFramebuffer *vfb) { force1x = vfb->bufferWidth <= 256 || vfb->bufferHeight <= 128; break; case 3: - force1x = vfb->bufferWidth < 480 || vfb->bufferWidth > 800 || vfb->bufferHeight < 272; // GOW uses ‭864‬x272 + force1x = vfb->bufferWidth < 480 || vfb->bufferWidth > 800 || vfb->bufferHeight < 272; // GOW uses 864x272 break; } diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index c2301004d3..a38e7f1fa3 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -24,13 +24,6 @@ MainWindow::MainWindow(QWidget *parent, bool fullscreen) : nextState(CORE_POWERDOWN), lastUIState(UISTATE_MENU) { - QDesktopWidget *desktop = QApplication::desktop(); - int screenNum = QProcessEnvironment::systemEnvironment().value("SDL_VIDEO_FULLSCREEN_HEAD", "0").toInt(); - - // Move window to the center of selected screen - QRect rect = desktop->screenGeometry(screenNum); - move((rect.width() - frameGeometry().width()) / 4, (rect.height() - frameGeometry().height()) / 4); - setWindowIcon(QIcon(qApp->applicationDirPath() + "/assets/icon_regular_72.png")); SetGameTitle(""); @@ -64,6 +57,9 @@ void MainWindow::newFrame() updateMenus(); } + if (g_Config.bFullScreen != isFullScreen()) + SetFullScreen(g_Config.bFullScreen); + std::unique_lock lock(msgMutex_); while (!msgQueue_.empty()) { MainWindowMsg msg = msgQueue_.front(); @@ -112,9 +108,6 @@ void MainWindow::updateMenus() void MainWindow::bootDone() { - if (g_Config.bFullScreen != isFullScreen()) - SetFullScreen(g_Config.bFullScreen); - if (nextState == CORE_RUNNING) runAct(); updateMenus(); @@ -389,6 +382,13 @@ void MainWindow::SetFullScreen(bool fullscreen) { if (GetUIState() == UISTATE_INGAME && QApplication::overrideCursor()) QApplication::restoreOverrideCursor(); + + QDesktopWidget *desktop = QApplication::desktop(); + int screenNum = QProcessEnvironment::systemEnvironment().value("SDL_VIDEO_FULLSCREEN_HEAD", "0").toInt(); + + // Move window to the center of selected screen + QRect rect = desktop->screenGeometry(screenNum); + move((rect.width() - frameGeometry().width()) / 4, (rect.height() - frameGeometry().height()) / 4); } } diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 937859af6e..feea1bbb1e 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -18,6 +18,7 @@ #include #include "ppsspp_config.h" + #include "Common/System/Display.h" #include "Common/System/NativeApp.h" #include "Common/System/System.h" @@ -37,6 +38,7 @@ #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Core/System.h" +#include "Core/Reporting.h" #include "Core/CoreParameter.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/JitCommon/JitBlockCache.h" @@ -89,6 +91,7 @@ void DevMenu::CreatePopupContents(UI::ViewGroup *parent) { items->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Handle(this, &DevMenu::OnFreezeFrame); items->Add(new Choice(dev->T("Dump Frame GPU Commands")))->OnClick.Handle(this, &DevMenu::OnDumpFrame); items->Add(new Choice(dev->T("Toggle Audio Debug")))->OnClick.Handle(this, &DevMenu::OnToggleAudioDebug); + items->Add(new Choice(dev->T("Reset limited logging")))->OnClick.Handle(this, &DevMenu::OnResetLimitedLogging); scroll->Add(items); @@ -105,6 +108,10 @@ UI::EventReturn DevMenu::OnToggleAudioDebug(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn DevMenu::OnResetLimitedLogging(UI::EventParams &e) { + Reporting::ResetCounts(); + return UI::EVENT_DONE; +} UI::EventReturn DevMenu::OnLogView(UI::EventParams &e) { UpdateUIState(UISTATE_PAUSEMENU); diff --git a/UI/DevScreens.h b/UI/DevScreens.h index 28e8c38bda..b97f08574b 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -45,6 +45,7 @@ protected: UI::EventReturn OnDumpFrame(UI::EventParams &e); UI::EventReturn OnDeveloperTools(UI::EventParams &e); UI::EventReturn OnToggleAudioDebug(UI::EventParams &e); + UI::EventReturn OnResetLimitedLogging(UI::EventParams &e); }; class JitDebugScreen : public UIDialogScreenWithBackground { diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 7216d628ea..ffb993d639 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -873,6 +873,7 @@ void GameSettingsScreen::CreateViews() { networkingSettings->Add(new PopupSliderChoice(&g_Config.iPortOffset, 0, 60000, n->T("Port offset", "Port offset (0 = PSP compatibility)"), 100, screenManager())); networkingSettings->Add(new PopupSliderChoice(&g_Config.iMinTimeout, 1, 15000, n->T("Minimum Timeout", "Minimum Timeout (override low latency in ms)"), 100, screenManager())); networkingSettings->Add(new CheckBox(&g_Config.bTCPNoDelay, n->T("TCP No Delay", "TCP No Delay (faster TCP)"))); + networkingSettings->Add(new CheckBox(&g_Config.bForcedFirstConnect, n->T("Forced First Connect", "Forced First Connect (faster Connect)"))); ViewGroup *toolsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); toolsScroll->SetTag("GameSettingsTools"); @@ -930,7 +931,10 @@ void GameSettingsScreen::CreateViews() { rewindFreq->SetZeroLabel(sy->T("Off")); systemSettings->Add(new CheckBox(&g_Config.bMemStickInserted, sy->T("Memory Stick inserted"))); + PopupSliderChoice *memStickSize = systemSettings->Add(new PopupSliderChoice(&g_Config.iMemStickSizeGB, 1, 32, sy->T("Change Memory Stick Size", "Change Memory Stick Size(GB)"), screenManager(), "GB")); + } + systemSettings->Add(new ItemHeader(sy->T("General"))); #if PPSSPP_PLATFORM(ANDROID) @@ -1924,7 +1928,7 @@ void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) { buttonsRow2->Add(new Button(di->T("Toggle List")))->OnClick.Handle(this, &HostnameSelectScreen::OnShowIPListClick); buttonsRow2->Add(new Spacer(new LinearLayoutParams(1.0, G_RIGHT))); - std::vector listIP = {"myneighborsushicat.com", "localhost"}; + std::vector listIP = {"myneighborsushicat.com", "socom.cc", "localhost"}; // TODO: Add some saved recent history too? net::GetIPList(listIP); ipRows_ = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0)); ScrollView* scrollView = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 94fc04123c..0b32747a52 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -595,8 +595,14 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch #endif if (!strncmp(argv[i], "--pause-menu-exit", strlen("--pause-menu-exit"))) g_Config.bPauseMenuExitsEmulator = true; - if (!strcmp(argv[i], "--fullscreen")) + if (!strcmp(argv[i], "--fullscreen")) { g_Config.bFullScreen = true; + System_SendMessage("toggle_fullscreen", "1"); + } + if (!strcmp(argv[i], "--windowed")) { + g_Config.bFullScreen = false; + System_SendMessage("toggle_fullscreen", "0"); + } if (!strcmp(argv[i], "--touchscreentest")) gotoTouchScreenTest = true; if (!strcmp(argv[i], "--gamesettings")) diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 5b89f78b57..e63eb37d08 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -22,8 +22,9 @@ void OnScreenMessagesView::Draw(UIContext &dc) { float y = 10.0f; // Then draw them all. const std::list &messages = osm.Messages(); + double now = time_now_d(); for (auto iter = messages.begin(); iter != messages.end(); ++iter) { - float alpha = (iter->endTime - time_now_d()) * 4.0f; + float alpha = (iter->endTime - now) * 4.0f; if (alpha > 1.0) alpha = 1.0f; if (alpha < 0.0) alpha = 0.0f; // Messages that are wider than the screen are left-aligned instead of centered. diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index e91446278e..f10b631a43 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -658,7 +658,7 @@ namespace MainWindow } lastMouseDown = 0.0; } else { - lastMouseDown = time_now_d(); + lastMouseDown = now; } } break; diff --git a/Windows/XinputDevice.h b/Windows/XinputDevice.h index 40cb973b25..fc640e708e 100644 --- a/Windows/XinputDevice.h +++ b/Windows/XinputDevice.h @@ -17,6 +17,6 @@ private: int check_delay[4]{}; XINPUT_STATE prevState[4]{}; XINPUT_VIBRATION prevVibration[4]{}; - int prevVibrationTime = 0; + double prevVibrationTime = 0.0; u32 prevButtons[4]{}; }; diff --git a/Windows/main.cpp b/Windows/main.cpp index 3abdd7dfb0..3ffe67259e 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -564,9 +564,6 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin break; } - if (wideArgs[i] == L"--windowed") - g_Config.bFullScreen = false; - if (wideArgs[i].find(gpuBackend) != std::wstring::npos && wideArgs[i].size() > gpuBackend.size()) { const std::wstring restOfOption = wideArgs[i].substr(gpuBackend.size()); diff --git a/assets/flash0/font/jpn0.pgf b/assets/flash0/font/jpn0.pgf index 7b2477666f..1553aecd86 100644 Binary files a/assets/flash0/font/jpn0.pgf and b/assets/flash0/font/jpn0.pgf differ diff --git a/assets/lang b/assets/lang index d5a2a51942..fd6f5bc01a 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit d5a2a51942377820764604d9bb424fa9a879c4bd +Subproject commit fd6f5bc01afb419eacca538b425c01eb2a055393 diff --git a/build.gradle b/build.gradle index dad9ef0a6c..11479e334c 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.1' + classpath 'com.android.tools.build:gradle:4.0.2' } } diff --git a/ext/snappy/snappy-stubs-internal.h b/ext/snappy/snappy-stubs-internal.h index 4854689d17..48698958e7 100644 --- a/ext/snappy/snappy-stubs-internal.h +++ b/ext/snappy/snappy-stubs-internal.h @@ -137,7 +137,7 @@ static const int64 kint64max = static_cast(0x7FFFFFFFFFFFFFFFLL); // and 32-bit values (not 64-bit); older versions either raise a fatal signal, // do an unaligned read and rotate the words around a bit, or do the reads very // slowly (trip through kernel mode). There's no simple #define that says just -// “ARMv7 or higher”, so we have to filter away all ARMv5 and ARMv6 +// "ARMv7 or higher", so we have to filter away all ARMv5 and ARMv6 // sub-architectures. // // This is a mess, but there's not much we can do about it. diff --git a/ext/snappy/snappy.cpp b/ext/snappy/snappy.cpp index ce1eef4630..74891a6a3f 100644 --- a/ext/snappy/snappy.cpp +++ b/ext/snappy/snappy.cpp @@ -384,12 +384,12 @@ static inline char* EmitCopyAtMost64(char* op, size_t offset, size_t len) { if (len_less_than_12 && SNAPPY_PREDICT_TRUE(offset < 2048)) { // offset fits in 11 bits. The 3 highest go in the top of the first byte, // and the rest go in the second byte. - *op++ = COPY_1_BYTE_OFFSET + ((len - 4) << 2) + ((offset >> 3) & 0xe0); + *op++ = (char)(COPY_1_BYTE_OFFSET + ((len - 4) << 2) + ((offset >> 3) & 0xe0)); *op++ = offset & 0xff; } else { // Write 4 bytes, though we only care about 3 of them. The output buffer // is required to have some slack, so the extra byte won't overrun it. - uint32 u = COPY_2_BYTE_OFFSET + ((len - 1) << 2) + (offset << 8); + uint32 u = (uint32)(COPY_2_BYTE_OFFSET + ((len - 1) << 2) + (offset << 8)); LittleEndian::Store32(op, u); op += 3; } @@ -458,7 +458,7 @@ uint32 CalculateTableSize(uint32 input_size) { namespace internal { WorkingMemory::WorkingMemory(size_t input_size) { const size_t max_fragment_size = std::min(input_size, kBlockSize); - const size_t table_size = CalculateTableSize(max_fragment_size); + const size_t table_size = CalculateTableSize((uint32)max_fragment_size); size_ = table_size * sizeof(*table_) + max_fragment_size + MaxCompressedLength(max_fragment_size); mem_ = std::allocator().allocate(size_); @@ -473,9 +473,9 @@ WorkingMemory::~WorkingMemory() { uint16* WorkingMemory::GetHashTable(size_t fragment_size, int* table_size) const { - const size_t htsize = CalculateTableSize(fragment_size); + const size_t htsize = CalculateTableSize((uint32)fragment_size); memset(table_, 0, htsize * sizeof(*table_)); - *table_size = htsize; + *table_size = (int)htsize; return table_; } } // end namespace internal @@ -602,7 +602,7 @@ char* CompressFragment(const char* input, assert(candidate >= base_ip); assert(candidate < ip); - table[hash] = ip - base_ip; + table[hash] = (uint16)(ip - base_ip); } while (SNAPPY_PREDICT_TRUE(UNALIGNED_LOAD32(ip) != UNALIGNED_LOAD32(candidate))); @@ -610,7 +610,7 @@ char* CompressFragment(const char* input, // than 4 bytes match. But, prior to the match, input // bytes [next_emit, ip) are unmatched. Emit them as "literal bytes." assert(next_emit + 16 <= ip_end); - op = EmitLiteral(op, next_emit, ip - next_emit); + op = EmitLiteral(op, next_emit, (int)(ip - next_emit)); // Step 3: Call EmitCopy, and then see if another EmitCopy could // be our next move. Repeat until we find no match for the @@ -647,11 +647,11 @@ char* CompressFragment(const char* input, // we also update table[Hash(ip - 1, shift)] and table[Hash(ip, shift)]. input_bytes = GetEightBytesAt(ip - 1); uint32 prev_hash = HashBytes(GetUint32AtOffset(input_bytes, 0), shift); - table[prev_hash] = ip - base_ip - 1; + table[prev_hash] = (uint16)(ip - base_ip - 1); uint32 cur_hash = HashBytes(GetUint32AtOffset(input_bytes, 1), shift); candidate = base_ip + table[cur_hash]; candidate_bytes = UNALIGNED_LOAD32(candidate); - table[cur_hash] = ip - base_ip; + table[cur_hash] = (uint16)(ip - base_ip); } while (GetUint32AtOffset(input_bytes, 1) == candidate_bytes); next_hash = HashBytes(GetUint32AtOffset(input_bytes, 2), shift); @@ -663,7 +663,7 @@ char* CompressFragment(const char* input, // Emit the remaining bytes as a literal if (next_emit < ip_end) { op = EmitLiteral(op, next_emit, - ip_end - next_emit); + (int)(ip_end - next_emit)); } return op; @@ -864,7 +864,7 @@ class SnappyDecompressor { // Long literal. const size_t literal_length_length = literal_length - 60; literal_length = - ExtractLowBytes(LittleEndian::Load32(ip), literal_length_length) + + ExtractLowBytes(LittleEndian::Load32(ip), (int)literal_length_length) + 1; ip += literal_length_length; } @@ -877,7 +877,7 @@ class SnappyDecompressor { size_t n; ip = reader_->Peek(&n); avail = n; - peeked_ = avail; + peeked_ = (uint32)avail; if (avail == 0) return; // Premature end of input ip_limit_ = ip + avail; } @@ -889,7 +889,7 @@ class SnappyDecompressor { } else { const size_t entry = char_table[c]; const size_t trailer = - ExtractLowBytes(LittleEndian::Load32(ip), entry >> 11); + ExtractLowBytes(LittleEndian::Load32(ip), (int)entry >> 11); const size_t length = entry & 0xff; ip += entry >> 11; @@ -915,7 +915,7 @@ bool SnappyDecompressor::RefillTag() { reader_->Skip(peeked_); // All peeked bytes are used up size_t n; ip = reader_->Peek(&n); - peeked_ = n; + peeked_ = (uint32)n; eof_ = (n == 0); if (eof_) return false; ip_limit_ = ip + n; @@ -929,7 +929,7 @@ bool SnappyDecompressor::RefillTag() { assert(needed <= sizeof(scratch_)); // Read more bytes from reader if needed - uint32 nbuf = ip_limit_ - ip; + uint32 nbuf = (uint32)(ip_limit_ - ip); if (nbuf < needed) { // Stitch together bytes from ip and reader to form the word // contents. We store the needed bytes in "scratch_". They @@ -942,7 +942,7 @@ bool SnappyDecompressor::RefillTag() { size_t length; const char* src = reader_->Peek(&length); if (length == 0) return false; - uint32 to_add = std::min(needed - nbuf, length); + uint32 to_add = std::min(needed - nbuf, (uint32)length); memcpy(scratch_ + nbuf, src, to_add); nbuf += to_add; reader_->Skip(to_add); @@ -972,7 +972,7 @@ static bool InternalUncompress(Source* r, Writer* writer) { uint32 uncompressed_len = 0; if (!decompressor.ReadUncompressedLength(&uncompressed_len)) return false; - return InternalUncompressAllTags(&decompressor, writer, r->Available(), + return InternalUncompressAllTags(&decompressor, writer, (uint32)r->Available(), uncompressed_len); } @@ -1001,7 +1001,7 @@ size_t Compress(Source* reader, Sink* writer) { size_t N = reader->Available(); const size_t uncompressed_size = N; char ulength[Varint::kMax32]; - char* p = Varint::Encode32(ulength, N); + char* p = Varint::Encode32(ulength, (uint32)N); writer->Append(ulength, p-ulength); written += (p - ulength); @@ -1043,7 +1043,7 @@ size_t Compress(Source* reader, Sink* writer) { uint16* table = wmem.GetHashTable(num_to_read, &table_size); // Compress input_fragment and append to dest - const int max_output = MaxCompressedLength(num_to_read); + const int max_output = (int)MaxCompressedLength(num_to_read); // Need a scratch buffer for the output, in case the byte sink doesn't // have room for us directly. @@ -1491,7 +1491,7 @@ class SnappyScatteredWriter { inline bool TryFastAppend(const char* ip, size_t available, size_t length) { char* op = op_ptr_; - const int space_left = op_limit_ - op; + const int space_left = (int)(op_limit_ - op); if (length <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16) { // Fast path, used for the majority (about 95%) of invocations. @@ -1507,7 +1507,7 @@ class SnappyScatteredWriter { char* const op_end = op_ptr_ + len; // See SnappyArrayWriter::AppendFromSelf for an explanation of // the "offset - 1u" trick. - if (SNAPPY_PREDICT_TRUE(offset - 1u < op_ptr_ - op_base_ && + if (SNAPPY_PREDICT_TRUE(offset - 1u < (size_t)(op_ptr_ - op_base_) && op_end <= op_limit_)) { // Fast path: src and dst in current block. op_ptr_ = IncrementalCopy(op_ptr_ - offset, op_ptr_, op_end, op_limit_); @@ -1540,7 +1540,7 @@ bool SnappyScatteredWriter::SlowAppend(const char* ip, size_t len) { // Make new block size_t bsize = std::min(kBlockSize, expected_ - full_size_); - op_base_ = allocator_.Allocate(bsize); + op_base_ = allocator_.Allocate((int)bsize); op_ptr_ = op_base_; op_limit_ = op_base_ + bsize; blocks_.push_back(op_base_); @@ -1647,13 +1647,13 @@ bool Uncompress(Source* compressed, Sink* uncompressed) { if (allocated_size >= uncompressed_len) { SnappyArrayWriter writer(buf); bool result = InternalUncompressAllTags(&decompressor, &writer, - compressed_len, uncompressed_len); + (uint32)compressed_len, uncompressed_len); uncompressed->Append(buf, writer.Produced()); return result; } else { SnappySinkAllocator allocator(uncompressed); SnappyScatteredWriter writer(allocator); - return InternalUncompressAllTags(&decompressor, &writer, compressed_len, + return InternalUncompressAllTags(&decompressor, &writer, (uint32)compressed_len, uncompressed_len); } } diff --git a/ext/xbrz/xbrz.cpp b/ext/xbrz/xbrz.cpp index 61fa7e3284..dc2feb2ba9 100644 --- a/ext/xbrz/xbrz.cpp +++ b/ext/xbrz/xbrz.cpp @@ -444,7 +444,7 @@ void blendPixel(const Kernel_3x3& ker, return true; //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes - if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90° corners + if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90?corners return false; if (getBottomL(blend) != BLEND_NONE && !eq(e, c)) return false; diff --git a/ext/xxhash.h b/ext/xxhash.h index 7fe59d9a3e..1790daf267 100644 --- a/ext/xxhash.h +++ b/ext/xxhash.h @@ -3115,7 +3115,7 @@ XXH3_initCustomSecret_avx512(void* XXH_RESTRICT customSecret, xxh_u64 seed64) int i; for (i=0; i < nbRounds; ++i) { /* GCC has a bug, _mm512_stream_load_si512 accepts 'void*', not 'void const*', - * this will warn "discards ‘const’ qualifier". */ + * this will warn "discards 'const' qualifier". */ union { XXH_ALIGN(64) const __m512i* cp; XXH_ALIGN(64) void* p; diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index edb5939cba..a310999360 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -23,6 +23,9 @@ // // TODO: Make a test of nice unittest asserts and count successes etc. // Or just integrate with an existing testing framework. +// +// To use, set command line parameter to one or more of the tests below, or "all". +// Search for "availableTests". #include #include @@ -308,10 +311,19 @@ bool TestVFPUSinCos() { EXPECT_APPROX_EQ_FLOAT(sine, 1.0f); EXPECT_APPROX_EQ_FLOAT(cosine, 0.0f); - for (float angle = -10.0f; angle < 10.0f; angle++) { + vfpu_sincos(-1.0f, sine, cosine); + EXPECT_EQ_FLOAT(sine, -1.0f); + EXPECT_EQ_FLOAT(cosine, 0.0f); + vfpu_sincos(-2.0f, sine, cosine); + EXPECT_EQ_FLOAT(sine, 0.0f); + EXPECT_EQ_FLOAT(cosine, -1.0f); + + for (float angle = -10.0f; angle < 10.0f; angle += 0.1f) { vfpu_sincos(angle, sine, cosine); EXPECT_APPROX_EQ_FLOAT(sine, sinf(angle * M_PI_2)); EXPECT_APPROX_EQ_FLOAT(cosine, cosf(angle * M_PI_2)); + + printf("sine: %f==%f cosine: %f==%f\n", sine, sinf(angle * M_PI_2), cosine, cosf(angle * M_PI_2)); } return true; }