diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index ad97a485cd..c7d32c5010 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -479,8 +479,8 @@ typedef struct SceNetAdhocMatchingContext { u64_le timeout; // Helper Thread (fake PSP Thread) needed to execute callback - HLEHelperThread *matchingThread; - SceUID matching_thid; + //HLEHelperThread *matchingThread; + int matching_thid; // Event Caller Thread std::thread eventThread; @@ -793,13 +793,19 @@ public: AfterAdhocMipsCall() {} static PSPAction* Create() { return new AfterAdhocMipsCall(); } void DoState(PointerWrap& p) override { - auto s = p.Section("AfterAdhocMipsCall", 3, 4); + auto s = p.Section("AfterAdhocMipsCall", 1, 4); if (!s) return; - - p.Do(HandlerID); - p.Do(EventID); - p.Do(argsAddr); + if (s >= 3) { + p.Do(HandlerID); + p.Do(EventID); + p.Do(argsAddr); + } + else { + HandlerID = -1; + EventID = -1; + argsAddr = 0; + } } void run(MipsCall& call) override; void SetData(int handlerID, int eventId, u32_le argsAddr); @@ -818,13 +824,20 @@ public: auto s = p.Section("AfterMatchingMipsCall", 1, 4); if (!s) return; - - p.Do(EventID); + if (s >= 1) { + p.Do(EventID); + } + else { + EventID = -1; + } if (s >= 4) { p.Do(contextID); p.Do(bufAddr); } - //context = NULL; + else { + contextID = -1; + bufAddr = 0; + } } void run(MipsCall &call) override; void SetData(int ContextID, int eventId, u32_le BufAddr); diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 8851347ec4..f307275835 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -47,8 +47,7 @@ static bool netInited; bool netInetInited; -bool netApctlInited; -int netApctlStatus; + u32 netDropRate = 0; u32 netDropDuration = 0; u32 netPoolAddr = 0; @@ -61,6 +60,34 @@ static std::map apctlHandlers; static struct SceNetApctlInfoInternal netApctlInfo; +bool netApctlInited; +u32 netApctlState; +u32 apctlThreadHackAddr = 0; +u32_le apctlThreadCode[3]; +SceUID apctlThreadID = 0; +int actionAfterApctlMipsCall; +std::recursive_mutex apctlEvtMtx; +std::deque apctlEvents; + +u32 Net_Term(); +int NetApctl_Term(); +void NetApctl_InitInfo(); + +void AfterApctlMipsCall::run(MipsCall& call) { + u32 v0 = currentMIPS->r[MIPS_REG_V0]; + DEBUG_LOG(SCENET, "AfterApctlMipsCall::run [ID=%i][OldState=%d][NewState=%d][Event=%d][Error=%d][ArgsPtr=%08x] [cbId: %u][retV0: %08x]", handlerID, oldState, newState, event, error, argsAddr, call.cbId, v0); + //call.setReturnValue(v0); +} + +void AfterApctlMipsCall::SetData(int HandlerID, int OldState, int NewState, int Event, int Error, u32_le ArgsAddr) { + handlerID = HandlerID; + oldState = OldState; + newState = NewState; + event = Event; + error = Error; + argsAddr = ArgsAddr; +} + static int InitLocalhostIP() { // find local IP addrinfo* localAddr; @@ -109,15 +136,33 @@ static int InitLocalhostIP() { return 0; } +void __NetApctlInit() { + netApctlInited = false; + netApctlState = PSP_NET_APCTL_STATE_DISCONNECTED; + apctlHandlers.clear(); + memset(&netApctlInfo, 0, sizeof(netApctlInfo)); +} + static void __ResetInitNetLib() { netInited = false; - netApctlInited = false; netInetInited = false; memset(&netMallocStat, 0, sizeof(netMallocStat)); memset(¶meter, 0, sizeof(parameter)); } +void __NetCallbackInit() { + // Init Network Callbacks + dummyThreadHackAddr = __CreateHLELoop(dummyThreadCode, "sceNetAdhoc", "__NetTriggerCallbacks", "dummythreadhack"); + matchingThreadHackAddr = __CreateHLELoop(matchingThreadCode, "sceNetAdhocMatching", "__NetMatchingCallbacks", "matchingThreadHack"); + apctlThreadHackAddr = __CreateHLELoop(apctlThreadCode, "sceNetApctl", "__NetApctlCallbacks", "apctlThreadHack"); + + // Newer one should be placed last to prevent callbacks going to the wrong after action after loading from old save state + actionAfterMatchingMipsCall = __KernelRegisterActionType(AfterMatchingMipsCall::Create); + actionAfterAdhocMipsCall = __KernelRegisterActionType(AfterAdhocMipsCall::Create); + actionAfterApctlMipsCall = __KernelRegisterActionType(AfterApctlMipsCall::Create); +} + void __NetInit() { // Windows: Assuming WSAStartup already called beforehand portOffset = g_Config.iPortOffset; @@ -134,35 +179,41 @@ void __NetInit() { __UPnPInit(); __ResetInitNetLib(); + __NetApctlInit(); + __NetCallbackInit(); +} + +void __NetApctlShutdown() { + if (apctlThreadHackAddr) { + kernelMemory.Free(apctlThreadHackAddr); + apctlThreadHackAddr = 0; + } } void __NetShutdown() { // Network Cleanup Net_Term(); + __NetApctlShutdown(); __ResetInitNetLib(); // Since PortManager supposed to be general purpose for whatever port forwarding PPSSPP needed, may be we shouldn't clear & restore ports in here? it will be cleared and restored by PortManager's destructor when exiting PPSSPP anyway __UPnPShutdown(); } -static void __UpdateApctlHandlers(int oldState, int newState, int flag, int error) { - u32 args[5] = { 0, 0, 0, 0, 0 }; - args[0] = oldState; - args[1] = newState; - args[2] = flag; - args[3] = error; +static void __UpdateApctlHandlers(u32 oldState, u32 newState, u32 flag, u32 error) { + std::lock_guard apctlGuard(apctlEvtMtx); + apctlEvents.push_back({ oldState, newState, flag, error }); +} - for(std::map::iterator it = apctlHandlers.begin(); it != apctlHandlers.end(); ++it) { - args[4] = it->second.argument; - // TODO: Need to make sure netApctlState is updated before calling the callback's mipscall so the game handler's subroutine can Get and make use the new state/info - hleEnqueueCall(it->second.entryPoint, 5, args); - } +// Make sure MIPS calls have been fully executed before the next notifyApctlHandlers +void notifyApctlHandlers(int oldState, int newState, int flag, int error) { + __UpdateApctlHandlers(oldState, newState, flag, error); } // This feels like a dubious proposition, mostly... void __NetDoState(PointerWrap &p) { - auto s = p.Section("sceNet", 1, 3); + auto s = p.Section("sceNet", 1, 4); if (!s) return; @@ -191,11 +242,32 @@ void __NetDoState(PointerWrap &p) { p.Do(netThread1Addr); p.Do(netThread2Addr); } + if (s >= 4) { + p.Do(netApctlState); + p.Do(netApctlInfo); + p.Do(actionAfterApctlMipsCall); + __KernelRestoreActionType(actionAfterApctlMipsCall, AfterApctlMipsCall::Create); + p.Do(apctlThreadHackAddr); + p.Do(apctlThreadID); + } + else { + actionAfterApctlMipsCall = -1; + apctlThreadHackAddr = 0; + apctlThreadID = 0; + } // Let's not change "Inited" value when Loading SaveState in the middle of multiplayer to prevent memory & port leaks if (p.mode == p.MODE_READ) { netApctlInited = cur_netApctlInited; netInetInited = cur_netInetInited; netInited = cur_netInited; + + // Previously, this wasn't being saved. It needs its own space. + if (!apctlThreadHackAddr || (apctlThreadHackAddr && strcmp("apctlThreadHack", kernelMemory.GetBlockTag(apctlThreadHackAddr)) != 0)) { + u32 blockSize = sizeof(apctlThreadCode); + apctlThreadHackAddr = kernelMemory.Alloc(blockSize, false, "apctlThreadHack"); + } + // Restore Apctl Loop MIPS code to prevent crashes after loading from SaveState + if (apctlThreadHackAddr) Memory::Memcpy(apctlThreadHackAddr, apctlThreadCode, sizeof(apctlThreadCode)); } } @@ -294,6 +366,101 @@ std::string error2str(u32 errorCode) { return str; } +void __NetApctlCallbacks() +{ + std::lock_guard apctlGuard(apctlEvtMtx); + int delayus = 10000; + + // 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]; + 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_SCAN_REQUEST) + delayus = (adhocEventDelayMS + 2 * adhocExtraPollDelayMS) * 1000; + else + delayus = (adhocEventPollDelayMS + 2 * adhocExtraPollDelayMS) * 1000; + + // Do we need to change the oldState? even if there was error? + //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) { + 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? + 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 }); + break; + + case PSP_NET_APCTL_EVENT_GET_IP: + netApctlState = PSP_NET_APCTL_STATE_GOT_IP; + NetApctl_InitInfo(); + break; + + case PSP_NET_APCTL_EVENT_DISCONNECT_REQUEST: + netApctlState = 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 }); + break; + + case PSP_NET_APCTL_EVENT_SCAN_COMPLETE: + netApctlState = 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 + 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 }); + 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 }); + break; + } + // Do we need to change the newState? even if there were error? + //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) { + actionAfterApctlMipsCall = __KernelRegisterActionType(AfterApctlMipsCall::Create); + } + + // 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); + args.data[4] = it->second.argument; + AfterApctlMipsCall* after = (AfterApctlMipsCall*)__KernelCreateAction(actionAfterApctlMipsCall); + after->SetData(it->first, *oldState, *newState, *event, *error, it->second.argument); + hleEnqueueCall(it->second.entryPoint, 5, args.data, after); + } + } + + // Must be delayed long enough whenever there is a pending callback. + hleDelayResult(0, "Prevent Apctl thread from blocking", delayus); +} + static inline u32 AllocUser(u32 size, bool fromTop, const char *name) { u32 addr = userMemory.Alloc(size, fromTop, name); if (addr == -1) @@ -313,7 +480,7 @@ u32 Net_Term() { NetAdhoc_Term(); // TODO: Not implemented yet - //sceNetApctl_Term(); + NetApctl_Term(); //NetInet_Term(); // Library is initialized @@ -548,24 +715,21 @@ int sceNetInetTerm() { return 0; } -static int sceNetApctlInit(int stackSize, int initPriority) { - ERROR_LOG(SCENET, "UNIMPL sceNetApctlInit()"); - if (netApctlInited) - return ERROR_NET_APCTL_ALREADY_INITIALIZED; - +void NetApctl_InitInfo() { memset(&netApctlInfo, 0, sizeof(netApctlInfo)); - // Set default values - truncate_cpy(netApctlInfo.name, sizeof(netApctlInfo.name), "Wifi"); - truncate_cpy(netApctlInfo.ssid, sizeof(netApctlInfo.ssid), "Wifi"); - memcpy(netApctlInfo.bssid, "\1\1\2\2\3\3", sizeof(netApctlInfo.bssid)); - netApctlInfo.ssidLength = 4; + // Set dummy/fake values, these probably not suppose to have valid info before connected to an AP, right? + std::string APname = "Wifi"; // fake AP/hotspot + truncate_cpy(netApctlInfo.name, sizeof(netApctlInfo.name), APname.c_str()); + truncate_cpy(netApctlInfo.ssid, sizeof(netApctlInfo.ssid), APname.c_str()); + memcpy(netApctlInfo.bssid, "\1\1\2\2\3\3", sizeof(netApctlInfo.bssid)); // fake AP's mac address + netApctlInfo.ssidLength = static_cast(APname.length()); netApctlInfo.strength = 99; netApctlInfo.channel = g_Config.iWlanAdhocChannel; if (netApctlInfo.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) netApctlInfo.channel = defaultWlanChannel; // Get Local IP Address sockaddr_in sockAddr; - getLocalIp(&sockAddr); - char ipstr[INET_ADDRSTRLEN] = "127.0.0.1"; // default + getLocalIp(&sockAddr); // This will be valid IP, we probably not suppose to have a valid IP before connected to any AP, right? + char ipstr[INET_ADDRSTRLEN] = "127.0.0.1"; // Patapon 3 seems to try to get current IP using ApctlGetInfo() right after ApctlInit(), what kind of IP should we use as default before ApctlConnect()? it shouldn't be a valid IP, right? inet_ntop(AF_INET, &sockAddr.sin_addr, ipstr, sizeof(ipstr)); truncate_cpy(netApctlInfo.ip, sizeof(netApctlInfo.ip), ipstr); // Change the last number to 1 to indicate a common dns server/internet gateway @@ -575,21 +739,51 @@ static int sceNetApctlInit(int stackSize, int initPriority) { truncate_cpy(netApctlInfo.primaryDns, sizeof(netApctlInfo.primaryDns), ipstr); truncate_cpy(netApctlInfo.secondaryDns, sizeof(netApctlInfo.secondaryDns), "8.8.8.8"); truncate_cpy(netApctlInfo.subNetMask, sizeof(netApctlInfo.subNetMask), "255.255.255.0"); +} - // TODO: Create APctl fake-Thread +static int sceNetApctlInit(int stackSize, int initPriority) { + WARN_LOG(SCENET, "UNTESTED %s(%i, %i)", __FUNCTION__, stackSize, initPriority); + if (netApctlInited) + return ERROR_NET_APCTL_ALREADY_INITIALIZED; + + // Set default value before connected to an AP + memset(&netApctlInfo, 0, sizeof(netApctlInfo)); // NetApctl_InitInfo(); + truncate_cpy(netApctlInfo.ip, sizeof(netApctlInfo.ip), "0.0.0.0"); + truncate_cpy(netApctlInfo.gateway, sizeof(netApctlInfo.gateway), "0.0.0.0"); + truncate_cpy(netApctlInfo.primaryDns, sizeof(netApctlInfo.primaryDns), "0.0.0.0"); + truncate_cpy(netApctlInfo.secondaryDns, sizeof(netApctlInfo.secondaryDns), "0.0.0.0"); + truncate_cpy(netApctlInfo.subNetMask, sizeof(netApctlInfo.subNetMask), "0.0.0.0"); + + // Create APctl fake-Thread + apctlThreadID = __KernelCreateThread("ApctlThread", __KernelGetCurThreadModuleId(), apctlThreadHackAddr, initPriority, stackSize, PSP_THREAD_ATTR_USER, 0, true); + if (apctlThreadID > 0) { + __KernelStartThread(apctlThreadID, 0, 0); + } netApctlInited = true; - netApctlStatus = PSP_NET_APCTL_STATE_DISCONNECTED; + netApctlState = PSP_NET_APCTL_STATE_DISCONNECTED; + + return 0; +} + +int NetApctl_Term() { + // Cleanup Apctl resources + // Delete fake PSP Thread + if (apctlThreadID != 0) { + __KernelStopThread(apctlThreadID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "ApctlThread stopped"); + __KernelDeleteThread(apctlThreadID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "ApctlThread deleted"); + apctlThreadID = 0; + } + + netApctlInited = false; + netApctlState = PSP_NET_APCTL_STATE_DISCONNECTED; return 0; } int sceNetApctlTerm() { - ERROR_LOG(SCENET, "UNIMPL sceNeApctlTerm()"); - netApctlInited = false; - netApctlStatus = PSP_NET_APCTL_STATE_DISCONNECTED; - - return 0; + WARN_LOG(SCENET, "UNTESTED %s()", __FUNCTION__); + return NetApctl_Term(); } static int sceNetApctlGetInfo(int code, u32 pInfoAddr) { @@ -829,8 +1023,7 @@ static int sceNetInetConnect(int socket, u32 sockAddrInternetPtr, int addressLen static int sceNetApctlConnect(int connIndex) { ERROR_LOG(SCENET, "UNIMPL %s(%i)", __FUNCTION__, connIndex); - - netApctlStatus = PSP_NET_APCTL_STATE_JOINING; + // Is this connIndex is the index to the scanning's result data or sceNetApctlGetBSSDescIDListUser result? __UpdateApctlHandlers(0, 0, PSP_NET_APCTL_EVENT_CONNECT_REQUEST, 0); return 0; } @@ -839,7 +1032,7 @@ static int sceNetApctlDisconnect() { ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); // Like its 'sister' function sceNetAdhocctlDisconnect, we need to alert Apctl handlers that a disconnect took place // or else games like Phantasy Star Portable 2 will hang at certain points (e.g. returning to the main menu after trying to connect to PSN). - netApctlStatus = PSP_NET_APCTL_STATE_DISCONNECTED; + __UpdateApctlHandlers(0, 0, PSP_NET_APCTL_EVENT_DISCONNECT_REQUEST, 0); return 0; } @@ -852,7 +1045,7 @@ static int sceNetApctlGetState(u32 pStateAddr) { // Valid Arguments if (Memory::IsValidAddress(pStateAddr)) { // Return Thread Status - Memory::Write_U32(netApctlStatus, pStateAddr); + Memory::Write_U32(netApctlState, pStateAddr); // Return Success return hleLogSuccessI(SCENET, 0); } @@ -864,10 +1057,9 @@ static int sceNetApctlScanUser() { ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); // Scan probably only works when not in connected state, right? - if (netApctlStatus != PSP_NET_APCTL_STATE_DISCONNECTED) + if (netApctlState != PSP_NET_APCTL_STATE_DISCONNECTED) return hleLogError(SCENET, ERROR_NET_APCTL_NOT_DISCONNECTED, "apctl not disconnected"); - netApctlStatus = PSP_NET_APCTL_STATE_SCANNING; __UpdateApctlHandlers(0, 0, PSP_NET_APCTL_EVENT_SCAN_REQUEST, 0); return 0; } @@ -947,10 +1139,9 @@ static int sceNetApctlScanSSID2() { ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); // Scan probably only works when not in connected state, right? - if (netApctlStatus != PSP_NET_APCTL_STATE_DISCONNECTED) + if (netApctlState != PSP_NET_APCTL_STATE_DISCONNECTED) return hleLogError(SCENET, ERROR_NET_APCTL_NOT_DISCONNECTED, "apctl not disconnected"); - netApctlStatus = PSP_NET_APCTL_STATE_SCANNING; __UpdateApctlHandlers(0, 0, PSP_NET_APCTL_EVENT_SCAN_REQUEST, 0); return 0; } @@ -1150,7 +1341,9 @@ const HLEFunction sceNetApctl[] = { {0XB3CF6849, &WrapI_V, "sceNetApctlScan", 'i', "" }, {0X0C7FFA5C, &WrapI_UU, "sceNetApctlGetBSSDescIDList", 'i', "xx" }, {0X96BEB231, &WrapI_IIU, "sceNetApctlGetBSSDescEntry", 'i', "iix" }, - {0XC20A144C, &WrapI_IU, "sceNetApctl_lib2_C20A144C", 'i', "ix" }, + {0XC20A144C, &WrapI_IU, "sceNetApctl_lib2_C20A144C", 'i', "ix" }, + // Fake function for PPSSPP's use. + {0X756E6F10, &WrapV_V<__NetApctlCallbacks>, "__NetApctlCallbacks", 'v', "" }, }; const HLEFunction sceWlanDrv[] = { diff --git a/Core/HLE/sceNet.h b/Core/HLE/sceNet.h index d8514491f8..1ca82af0d5 100644 --- a/Core/HLE/sceNet.h +++ b/Core/HLE/sceNet.h @@ -20,6 +20,14 @@ #include #include "Core/HLE/proAdhoc.h" +// Using constants instead of numbers for readability reason, since PSP_THREAD_ATTR_KERNEL/USER is located in sceKernelThread.cpp instead of sceKernelThread.h +#ifndef PSP_THREAD_ATTR_KERNEL +#define PSP_THREAD_ATTR_KERNEL 0x00001000 +#endif +#ifndef PSP_THREAD_ATTR_USER +#define PSP_THREAD_ATTR_USER 0x80000000 +#endif + // Option Names #define PSP_SO_REUSEPORT 0x0200 #define PSP_SO_NBIO 0x1009 @@ -186,11 +194,52 @@ struct ApctlHandler { u32 argument; }; +struct ApctlArgs { + u32_le data[5]; // OldState, NewState, Event, Error, ArgsAddr +}; + class PointerWrap; +class AfterApctlMipsCall : public PSPAction { +public: + AfterApctlMipsCall() {} + static PSPAction* Create() { return new AfterApctlMipsCall(); } + void DoState(PointerWrap& p) override { + auto s = p.Section("AfterApctlMipsCall", 1, 1); + if (!s) + return; + // Just in case there are "s" corruption in the future where s.ver is a negative number + if (s >= 1) { + p.Do(handlerID); + p.Do(oldState); + p.Do(newState); + p.Do(event); + p.Do(error); + p.Do(argsAddr); + } + else { + handlerID = -1; + oldState = 0; + newState = 0; + event = 0; + error = 0; + argsAddr = 0; + } + } + void run(MipsCall& call) override; + void SetData(int HandlerID, int OldState, int NewState, int Event, int Error, u32_le ArgsAddr); + +private: + int handlerID = -1; + int oldState = 0; + int newState = 0; + int event = 0; + int error = 0; + u32_le argsAddr = 0; +}; + extern bool netInetInited; extern bool netApctlInited; -extern int netApctlStatus; template std::string num2hex(I w, size_t hex_len = sizeof(I) << 1); std::string error2str(u32 errorcode); @@ -205,8 +254,6 @@ void __NetInit(); void __NetShutdown(); void __NetDoState(PointerWrap &p); -u32 Net_Term(); - int sceNetInetPoll(void *fds, u32 nfds, int timeout); int sceNetInetTerm(); int sceNetApctlTerm(); diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 1c68dfbacc..87f7075025 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -45,14 +45,6 @@ #include "Core/HLE/proAdhocServer.h" #include "i18n/i18n.h" -// Using constants instead of numbers for readability reason, since PSP_THREAD_ATTR_KERNEL/USER is located in sceKernelThread.cpp instead of sceKernelThread.h -#ifndef PSP_THREAD_ATTR_KERNEL -#define PSP_THREAD_ATTR_KERNEL 0x00001000 -#endif -#ifndef PSP_THREAD_ATTR_USER -#define PSP_THREAD_ATTR_USER 0x80000000 -#endif - // shared in sceNetAdhoc.h since it need to be used from sceNet.cpp also // TODO: Make accessor functions instead, and throw all this state in a struct. bool netAdhocInited; @@ -73,6 +65,7 @@ std::recursive_mutex adhocEvtMtx; std::deque> adhocctlEvents; std::deque matchingEvents; std::map adhocctlHandlers; +std::vector matchingThreads; int IsAdhocctlInCB = 0; u32 dummyThreadHackAddr = 0; @@ -118,7 +111,7 @@ void __NetAdhocShutdown() { } void __NetAdhocDoState(PointerWrap &p) { - auto s = p.Section("sceNetAdhoc", 1, 3); + auto s = p.Section("sceNetAdhoc", 1, 4); if (!s) return; @@ -137,21 +130,38 @@ void __NetAdhocDoState(PointerWrap &p) { p.Do(dummyThreadHackAddr); } + else { + actionAfterMatchingMipsCall = -1; + dummyThreadHackAddr = 0; + } if (s >= 3) { - //p.Do(IsAdhocctlInCB); // This will cause a crash if adhocEvtMtx was locked p.Do(actionAfterAdhocMipsCall); __KernelRestoreActionType(actionAfterAdhocMipsCall, AfterAdhocMipsCall::Create); p.Do(matchingThreadHackAddr); } + else { + actionAfterAdhocMipsCall = -1; + matchingThreadHackAddr = 0; + } + if (s >= 4) { + p.Do(threadAdhocID); + p.Do(matchingThreads); + } + else { + threadAdhocID = 0; + for (auto& it : matchingThreads) { + it = 0; + } + } if (p.mode == p.MODE_READ) { // Previously, this wasn't being saved. It needs its own space. - if (dummyThreadHackAddr && strcmp("dummythreadhack", kernelMemory.GetBlockTag(dummyThreadHackAddr)) != 0) { + if (!dummyThreadHackAddr || (dummyThreadHackAddr && strcmp("dummythreadhack", kernelMemory.GetBlockTag(dummyThreadHackAddr)) != 0)) { u32 blockSize = sizeof(dummyThreadCode); dummyThreadHackAddr = kernelMemory.Alloc(blockSize, false, "dummythreadhack"); } - if (matchingThreadHackAddr && strcmp("matchingThreadHack", kernelMemory.GetBlockTag(matchingThreadHackAddr)) != 0) { + if (!matchingThreadHackAddr || (matchingThreadHackAddr && strcmp("matchingThreadHack", kernelMemory.GetBlockTag(matchingThreadHackAddr)) != 0)) { u32 blockSize = sizeof(matchingThreadCode); matchingThreadHackAddr = kernelMemory.Alloc(blockSize, false, "matchingThreadHack"); } @@ -207,12 +217,6 @@ void __NetAdhocInit() { adhocctlHandlers.clear(); __AdhocServerInit(); - // Init Adhoc Callbacks - dummyThreadHackAddr = __CreateHLELoop(dummyThreadCode, "sceNetAdhoc", "__NetTriggerCallbacks", "dummythreadhack"); - matchingThreadHackAddr = __CreateHLELoop(matchingThreadCode, "sceNetAdhocMatching", "__NetMatchingCallbacks", "matchingThreadHack"); - actionAfterMatchingMipsCall = __KernelRegisterActionType(AfterMatchingMipsCall::Create); - actionAfterAdhocMipsCall = __KernelRegisterActionType(AfterAdhocMipsCall::Create); - // Create built-in AdhocServer Thread if (g_Config.bEnableWlan && g_Config.bEnableAdhocServer) { adhocServerRunning = true; @@ -2874,16 +2878,16 @@ int NetAdhocMatching_Stop(int matchingId) { } // Stop fake PSP Thread - if (item->matching_thid > 0) { - __KernelStopThread(item->matching_thid, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching stopped"); - __KernelDeleteThread(item->matching_thid, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching deleted"); + if (matchingThreads[item->matching_thid] > 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; }*/ } - item->matching_thid = 0; + matchingThreads[item->matching_thid] = 0; // Multithreading Lock peerlock.lock(); @@ -3013,6 +3017,7 @@ int NetAdhocMatching_Term() { context = next; } contexts = NULL; + matchingThreads.clear(); } return 0; @@ -3113,6 +3118,8 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle // Add Callback Handler context->handler.entryPoint = callbackAddr; + context->matching_thid = static_cast(matchingThreads.size()); + matchingThreads.push_back(0); // Link Context //context->connected = true; @@ -3159,84 +3166,7 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED; } -// This should be similar with sceNetAdhocMatchingStart2 but using USER_PARTITION_ID (2) for PartitionId params -static int sceNetAdhocMatchingStart(int matchingId, int evthPri, int evthStack, int inthPri, int inthStack, int optLen, u32 optDataAddr) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingStart(%i, %i, %i, %i, %i, %i, %08x) at %08x", matchingId, evthPri, evthStack, inthPri, inthStack, optLen, optDataAddr, currentMIPS->pc); - if (!g_Config.bEnableWlan) - return -1; - - // Multithreading Lock - peerlock.lock(); - - SceNetAdhocMatchingContext * item = findMatchingContext(matchingId); - - if (item != NULL) { - //sceNetAdhocMatchingSetHelloOpt(matchingId, optLen, optDataAddr); //SetHelloOpt only works when context is running - if ((optLen > 0) && Memory::IsValidAddress(optDataAddr)) { - // Allocate the memory and copy the content - if (item->hello != NULL) free(item->hello); - item->hello = (uint8_t *)malloc(optLen); - if (item->hello != NULL) { - Memory::Memcpy(item->hello, optDataAddr, optLen); - item->hellolen = optLen; - item->helloAddr = optDataAddr; - } - //else return ERROR_NET_ADHOC_MATCHING_NO_SPACE; //Faking success to prevent GTA:VCS from stuck unable to choose host/join menu - } - //else return ERROR_NET_ADHOC_MATCHING_INVALID_ARG; // ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN; // Returning Not Success will cause GTA:VC stuck unable to choose host/join menu - - //Add your own MAC as a member (only if it's empty?) - /*SceNetAdhocMatchingMemberInternal * peer = addMember(item, &item->mac); - switch (item->mode) { - case PSP_ADHOC_MATCHING_MODE_PARENT: - peer->state = PSP_ADHOC_MATCHING_PEER_OFFER; - break; - case PSP_ADHOC_MATCHING_MODE_CHILD: - peer->state = PSP_ADHOC_MATCHING_PEER_CHILD; - break; - case PSP_ADHOC_MATCHING_MODE_P2P: - peer->state = PSP_ADHOC_MATCHING_PEER_P2P; - }*/ - - // Create & Start the Fake PSP Thread ("matching_ev%d" and "matching_io%d") - std::string thrname = std::string("MatchingThr") + std::to_string(matchingId); - item->matching_thid = sceKernelCreateThread(thrname.c_str(), matchingThreadHackAddr, evthPri, evthStack, 0, 0); - //item->matchingThread = new HLEHelperThread(thrname.c_str(), "sceNetAdhocMatching", "__NetMatchingCallbacks", inthPri, inthStack); - if (item->matching_thid > 0) { - sceKernelStartThread(item->matching_thid, 0, 0); //sceKernelStartThread(context->event_thid, sizeof(context), &context); - //item->matchingThread->Start(matchingId, 0); - } - - //Create the threads - if (!item->eventRunning) { - item->eventRunning = true; - item->eventThread = std::thread(matchingEventThread, matchingId); - } - if (!item->inputRunning) { - item->inputRunning = true; - item->inputThread = std::thread(matchingInputThread, matchingId); - } - - item->running = 1; - netAdhocMatchingStarted++; - } - //else return ERROR_NET_ADHOC_MATCHING_INVALID_ID; //Faking success to prevent GTA:VCS from stuck unable to choose host/join menu - - // Multithreading Unlock - peerlock.unlock(); - - sleep_ms(adhocMatchingEventDelayMS); - //hleDelayResult(0, "give some time", adhocMatchingEventDelayMS * 1000); // 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? - - return 0; -} - -// With params for Partition ID for the event & input handler stack -static int sceNetAdhocMatchingStart2(int matchingId, int evthPri, int evthPartitionId, int evthStack, int inthPri, int inthPartitionId, int inthStack, int optLen, u32 optDataAddr) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingStart2(%i, %i, %i, %i, %i, %i, %i, %i, %08x) at %08x", matchingId, evthPri, evthPartitionId, evthStack, inthPri, inthPartitionId, inthStack, optLen, optDataAddr, currentMIPS->pc); - if (!g_Config.bEnableWlan) - return -1; - +int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int evthStack, int inthPri, int inthPartitionId, int inthStack, int optLen, u32 optDataAddr) { // Multithreading Lock peerlock.lock(); @@ -3270,13 +3200,13 @@ static int sceNetAdhocMatchingStart2(int matchingId, int evthPri, int evthPartit peer->state = PSP_ADHOC_MATCHING_PEER_P2P; }*/ - // Create & Start the Fake PSP Thread + // Create & Start the Fake PSP Thread ("matching_ev%d" and "matching_io%d") std::string thrname = std::string("MatchingThr") + std::to_string(matchingId); - item->matching_thid = __KernelCreateThread(thrname.c_str(), __KernelGetCurThreadModuleId(), matchingThreadHackAddr, evthPri, evthStack, PSP_THREAD_ATTR_USER, 0, false); - //item->matchingThread = new HLEHelperThread(thrname, "sceNetAdhocMatching", "AdhocMatchingFunc", inthPri, inthStack); - if (item->matching_thid > 0) { - __KernelStartThread(item->matching_thid, 0, 0); - //item->matchingThread->Start(0, 0); + matchingThreads[item->matching_thid] = sceKernelCreateThread(thrname.c_str(), matchingThreadHackAddr, evthPri, evthStack, 0, 0); + //item->matchingThread = new HLEHelperThread(thrname.c_str(), "sceNetAdhocMatching", "__NetMatchingCallbacks", inthPri, inthStack); + if (matchingThreads[item->matching_thid] > 0) { + sceKernelStartThread(matchingThreads[item->matching_thid], 0, 0); //sceKernelStartThread(context->event_thid, sizeof(context), &context); + //item->matchingThread->Start(matchingId, 0); } //Create the threads @@ -3298,11 +3228,32 @@ static int sceNetAdhocMatchingStart2(int matchingId, int evthPri, int evthPartit peerlock.unlock(); sleep_ms(adhocMatchingEventDelayMS); - //hleDelayResult(0,"give some time",adhocMatchingEventDelayMS*1000); // 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? + //hleDelayResult(0, "give some time", adhocMatchingEventDelayMS * 1000); // 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? return 0; } +#define KERNEL_PARTITION_ID 1 +#define USER_PARTITION_ID 2 +#define VSHELL_PARTITION_ID 5 +// This should be similar with sceNetAdhocMatchingStart2 but using USER_PARTITION_ID (2) for PartitionId params +static int sceNetAdhocMatchingStart(int matchingId, int evthPri, int evthStack, int inthPri, int inthStack, int optLen, u32 optDataAddr) { + WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingStart(%i, %i, %i, %i, %i, %i, %08x) at %08x", matchingId, evthPri, evthStack, inthPri, inthStack, optLen, optDataAddr, currentMIPS->pc); + if (!g_Config.bEnableWlan) + return -1; + + return NetAdhocMatching_Start(matchingId, evthPri, USER_PARTITION_ID, evthStack, inthPri, USER_PARTITION_ID, inthStack, optLen, optDataAddr); +} + +// With params for Partition ID for the event & input handler stack +static int sceNetAdhocMatchingStart2(int matchingId, int evthPri, int evthPartitionId, int evthStack, int inthPri, int inthPartitionId, int inthStack, int optLen, u32 optDataAddr) { + WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingStart2(%i, %i, %i, %i, %i, %i, %i, %i, %08x) at %08x", matchingId, evthPri, evthPartitionId, evthStack, inthPri, inthPartitionId, inthStack, optLen, optDataAddr, currentMIPS->pc); + if (!g_Config.bEnableWlan) + return -1; + + return NetAdhocMatching_Start(matchingId, evthPri, evthPartitionId, evthStack, inthPri, inthPartitionId, inthStack, optLen, optDataAddr); +} + static int sceNetAdhocMatchingSelectTarget(int matchingId, const char *macAddress, int optLen, u32 optDataPtr) { WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingSelectTarget(%i, %s, %i, %08x) at %08x", matchingId, mac2str((SceNetEtherAddr*)macAddress).c_str(), optLen, optDataPtr, currentMIPS->pc); @@ -4033,6 +3984,10 @@ void __NetTriggerCallbacks() //if (/*__KernelGetCurThread() == threadAdhocID &&*/ (!__IsInInterrupt() && __KernelIsDispatchEnabled() && !__KernelInCallback()) && IsAdhocctlInCallback() == 0) { + // Since 0 is a valid index to types_ we use -1 to detects if it was loaded from an old save state + if (actionAfterAdhocMipsCall < 0) { + actionAfterAdhocMipsCall = __KernelRegisterActionType(AfterAdhocMipsCall::Create); + } for (std::map::iterator it = adhocctlHandlers.begin(); it != adhocctlHandlers.end(); ++it) { DEBUG_LOG(SCENET, "AdhocctlCallback: [ID=%i][EVENT=%i]", it->first, flags); args[2] = it->second.argument; @@ -4067,6 +4022,9 @@ void __NetMatchingCallbacks() //(int matchingId) //if (/*__KernelGetCurThread() == context->matching_thid &&*/ (!__IsInInterrupt() && __KernelIsDispatchEnabled() && !__KernelInCallback()) /*&& !IsMatchingInCallback(context)*/) { + if (actionAfterMatchingMipsCall < 0) { + actionAfterMatchingMipsCall = __KernelRegisterActionType(AfterMatchingMipsCall::Create); + } DEBUG_LOG(SCENET, "AdhocMatchingCallback: [ID=%i][EVENT=%i][%s]", args[0], args[1], mac2str((SceNetEtherAddr*)Memory::GetPointer(args[2])).c_str()); AfterMatchingMipsCall* after = (AfterMatchingMipsCall*)__KernelCreateAction(actionAfterMatchingMipsCall); after->SetData(args[0], args[1], args[2]); diff --git a/Core/HLE/sceNetAdhoc.h b/Core/HLE/sceNetAdhoc.h index 75f8b71541..68fb4e2bcc 100644 --- a/Core/HLE/sceNetAdhoc.h +++ b/Core/HLE/sceNetAdhoc.h @@ -48,12 +48,18 @@ 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 std::recursive_mutex adhocEvtMtx; extern int IsAdhocctlInCB; +extern u32 dummyThreadHackAddr; +extern u32_le dummyThreadCode[3]; +extern u32 matchingThreadHackAddr; +extern u32_le matchingThreadCode[3]; + int NetAdhocMatching_Term(); int NetAdhocctl_Term(); int NetAdhoc_Term();