// Copyright (c) 2012- PPSSPP Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0 or later versions. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #if __linux__ || __APPLE__ #include #include #include #include #endif #include "Common/Net/Resolve.h" #include "Common/Data/Text/Parsers.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Common/Serialize/SerializeMap.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/HLE/sceKernelMemory.h" #include "Core/MIPS/MIPS.h" #include "Core/Config.h" #include "Core/MemMapHelpers.h" #include "Core/Util/PortManager.h" #include "sceKernel.h" #include "sceKernelThread.h" #include "sceKernelMutex.h" #include "sceUtility.h" #include "Core/HLE/proAdhoc.h" #include "Core/HLE/sceNetAdhoc.h" #include "Core/HLE/sceNet.h" #include "Core/HLE/sceNp.h" #include "Core/Reporting.h" #include "Core/Instance.h" static bool netInited; bool netInetInited; u32 netDropRate = 0; u32 netDropDuration = 0; u32 netPoolAddr = 0; u32 netThread1Addr = 0; u32 netThread2Addr = 0; static struct SceNetMallocStat netMallocStat; static std::map apctlHandlers; SceNetApctlInfoInternal netApctlInfo; bool netApctlInited; u32 netApctlState; u32 apctlThreadHackAddr = 0; u32_le apctlThreadCode[3]; SceUID apctlThreadID = 0; int apctlStateEvent = -1; int actionAfterApctlMipsCall; std::recursive_mutex apctlEvtMtx; std::deque apctlEvents; u32 Net_Term(); int NetApctl_Term(); void NetApctl_InitInfo(); void AfterApctlMipsCall::DoState(PointerWrap & p) { 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) { Do(p, handlerID); Do(p, oldState); Do(p, newState); Do(p, event); Do(p, error); Do(p, argsAddr); } else { handlerID = -1; oldState = 0; newState = 0; event = 0; error = 0; argsAddr = 0; } } 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; } void InitLocalhostIP() { // The entire 127.*.*.* is reserved for loopback. uint32_t localIP = 0x7F000001 + PPSSPP_ID - 1; g_localhostIP.in.sin_family = AF_INET; g_localhostIP.in.sin_addr.s_addr = htonl(localIP); g_localhostIP.in.sin_port = 0; std::string serverStr = StripSpaces(g_Config.proAdhocServer); 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) { WARN_LOG(SCENET, "sceNetApctl State WaitID(%i) on Thread(%i) already woken up? (error: %d)", uid, threadID, error); 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)); } static void __ResetInitNetLib() { netInited = 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; isOriPort = g_Config.bEnableUPnP && g_Config.bUPnPUseOriginalPort; minSocketTimeoutUS = g_Config.iMinTimeout * 1000UL; // Init Default AdhocServer struct g_adhocServerIP.in.sin_family = AF_INET; g_adhocServerIP.in.sin_port = htons(SERVER_PORT); //27312 // Maybe read this from config too g_adhocServerIP.in.sin_addr.s_addr = INADDR_NONE; InitLocalhostIP(); SceNetEtherAddr mac; getLocalMac(&mac); NOTICE_LOG(SCENET, "LocalHost IP will be %s [%s]", inet_ntoa(g_localhostIP.in.sin_addr), mac2str(&mac).c_str()); // TODO: May be we should initialize & cleanup somewhere else than here for PortManager to be used as general purpose for whatever port forwarding PPSSPP needed __UPnPInit(); __ResetInitNetLib(); __NetApctlInit(); __NetCallbackInit(); } void __NetApctlShutdown() { if (apctlThreadHackAddr) { kernelMemory.Free(apctlThreadHackAddr); apctlThreadHackAddr = 0; } apctlHandlers.clear(); apctlEvents.clear(); } 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(u32 oldState, u32 newState, u32 flag, u32 error) { std::lock_guard apctlGuard(apctlEvtMtx); apctlEvents.push_back({ oldState, newState, flag, error }); } // 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); } void netValidateLoopMemory() { // Allocate Memory if it wasn't valid/allocated after loaded from old SaveState if (!apctlThreadHackAddr || (apctlThreadHackAddr && strcmp("apctlThreadHack", kernelMemory.GetBlockTag(apctlThreadHackAddr)) != 0)) { u32 blockSize = sizeof(apctlThreadCode); apctlThreadHackAddr = kernelMemory.Alloc(blockSize, false, "apctlThreadHack"); if (apctlThreadHackAddr) Memory::Memcpy(apctlThreadHackAddr, apctlThreadCode, sizeof(apctlThreadCode)); } } // This feels like a dubious proposition, mostly... void __NetDoState(PointerWrap &p) { auto s = p.Section("sceNet", 1, 5); if (!s) return; auto cur_netInited = netInited; auto cur_netInetInited = netInetInited; auto cur_netApctlInited = netApctlInited; Do(p, netInited); Do(p, netInetInited); Do(p, netApctlInited); Do(p, apctlHandlers); Do(p, netMallocStat); if (s < 2) { netDropRate = 0; netDropDuration = 0; } else { Do(p, netDropRate); Do(p, netDropDuration); } if (s < 3) { netPoolAddr = 0; netThread1Addr = 0; netThread2Addr = 0; } else { Do(p, netPoolAddr); Do(p, netThread1Addr); Do(p, netThread2Addr); } if (s >= 4) { Do(p, netApctlState); Do(p, netApctlInfo); Do(p, actionAfterApctlMipsCall); if (actionAfterApctlMipsCall != -1) { __KernelRestoreActionType(actionAfterApctlMipsCall, AfterApctlMipsCall::Create); } Do(p, apctlThreadHackAddr); Do(p, apctlThreadID); } else { actionAfterApctlMipsCall = -1; 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 netApctlInited = cur_netApctlInited; netInetInited = cur_netInetInited; netInited = cur_netInited; // Discard leftover events apctlEvents.clear(); } } template std::string num2hex(I w, size_t hex_len) { static const char* digits = "0123456789ABCDEF"; std::string rc(hex_len, '0'); for (size_t i = 0, j = (hex_len - 1) * 4; i < hex_len; ++i, j -= 4) rc[i] = digits[(w >> j) & 0x0f]; return rc; } std::string error2str(u32 errorCode) { std::string str = ""; if (((errorCode >> 31) & 1) != 0) str += "ERROR "; if (((errorCode >> 30) & 1) != 0) str += "CRITICAL "; switch ((errorCode >> 16) & 0xfff) { case 0x41: str += "NET "; break; default: str += "UNK"+num2hex(u16((errorCode >> 16) & 0xfff), 3)+" "; } switch ((errorCode >> 8) & 0xff) { case 0x00: str += "COMMON "; break; case 0x01: str += "CORE "; break; case 0x02: str += "INET "; break; case 0x03: str += "POECLIENT "; break; case 0x04: str += "RESOLVER "; break; case 0x05: str += "DHCP "; break; case 0x06: str += "ADHOC_AUTH "; break; case 0x07: str += "ADHOC "; break; case 0x08: str += "ADHOC_MATCHING "; break; case 0x09: str += "NETCNF "; break; case 0x0a: str += "APCTL "; break; case 0x0b: str += "ADHOCCTL "; break; case 0x0c: str += "UNKNOWN1 "; break; case 0x0d: str += "WLAN "; break; case 0x0e: str += "EAPOL "; break; case 0x0f: str += "8021x "; break; case 0x10: str += "WPA "; break; case 0x11: str += "UNKNOWN2 "; break; case 0x12: str += "TRANSFER "; break; case 0x13: str += "ADHOC_DISCOVER "; break; case 0x14: str += "ADHOC_DIALOG "; break; case 0x15: str += "WISPR "; break; default: str += "UNKNOWN"+num2hex(u8((errorCode >> 8) & 0xff))+" "; } str += num2hex(u8(errorCode & 0xff)); return str; } 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]; apctlEvents.pop_front(); // Adjust delay according to current event. if (event == PSP_NET_APCTL_EVENT_CONNECT_REQUEST || event == PSP_NET_APCTL_EVENT_GET_IP || event == PSP_NET_APCTL_EVENT_SCAN_REQUEST) delayus = adhocEventDelay; else delayus = adhocEventPollDelay; // 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, right? //error = 0; switch (event) { case PSP_NET_APCTL_EVENT_CONNECT_REQUEST: newState = PSP_NET_APCTL_STATE_JOINING; // Should we set the State to PSP_NET_APCTL_STATE_DISCONNECTED if there was error? if (error == 0) apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); // Should we use PSP_NET_APCTL_EVENT_EAP_AUTH if securityType is not NONE? break; case PSP_NET_APCTL_EVENT_ESTABLISHED: newState = PSP_NET_APCTL_STATE_GETTING_IP; if (error == 0) apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_GET_IP, 0 }); break; case PSP_NET_APCTL_EVENT_GET_IP: newState = PSP_NET_APCTL_STATE_GOT_IP; NetApctl_InitInfo(); break; case PSP_NET_APCTL_EVENT_DISCONNECT_REQUEST: newState = PSP_NET_APCTL_STATE_DISCONNECTED; break; case PSP_NET_APCTL_EVENT_SCAN_REQUEST: newState = PSP_NET_APCTL_STATE_SCANNING; if (error == 0) apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_SCAN_COMPLETE, 0 }); break; case PSP_NET_APCTL_EVENT_SCAN_COMPLETE: newState = PSP_NET_APCTL_STATE_DISCONNECTED; break; case PSP_NET_APCTL_EVENT_EAP_AUTH: // Is this suppose to happen between JOINING and ESTABLISHED ? newState = PSP_NET_APCTL_STATE_EAP_AUTH; if (error == 0) apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_KEY_EXCHANGE, 0 }); // not sure if KEY_EXCHANGE is the next step after AUTH or not tho break; case PSP_NET_APCTL_EVENT_KEY_EXCHANGE: // Is this suppose to happen between JOINING and ESTABLISHED ? newState = PSP_NET_APCTL_STATE_KEY_EXCHANGE; if (error == 0) apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); break; case PSP_NET_APCTL_EVENT_RECONNECT: newState = PSP_NET_APCTL_STATE_DISCONNECTED; if (error == 0) apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_CONNECT_REQUEST, 0 }); 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=%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); 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; } // Must be delayed long enough whenever there is a pending callback to make sure previous callback & it's afterAction are fully executed sceKernelDelayThread(delayus); } static inline u32 AllocUser(u32 size, bool fromTop, const char *name) { u32 addr = userMemory.Alloc(size, fromTop, name); if (addr == -1) return 0; return addr; } static inline void FreeUser(u32 &addr) { if (addr != 0) userMemory.Free(addr); addr = 0; } u32 Net_Term() { // May also need to Terminate netAdhocctl and netAdhoc to free some resources & threads, since the game (ie. GTA:VCS, Wipeout Pulse, etc) might not called them before calling sceNetTerm and causing them to behave strangely on the next sceNetInit & sceNetAdhocInit NetAdhocctl_Term(); NetAdhoc_Term(); // TODO: Not implemented yet NetApctl_Term(); //NetInet_Term(); // Library is initialized if (netInited) { // Delete Adhoc Sockets deleteAllAdhocSockets(); // Delete GameMode Buffer //deleteAllGMB(); // Terminate Internet Library //sceNetInetTerm(); // Unload Internet Modules (Just keep it in memory... unloading crashes?!) // if (_manage_modules != 0) sceUtilityUnloadModule(PSP_MODULE_NET_INET); // Library shutdown } FreeUser(netPoolAddr); FreeUser(netThread1Addr); FreeUser(netThread2Addr); netInited = false; return 0; } static u32 sceNetTerm() { WARN_LOG(SCENET, "sceNetTerm()"); int retval = Net_Term(); // Give time to make sure everything are cleaned up hleEatMicro(adhocDefaultDelay); return retval; } /* Parameters: poolsize - Memory pool size (appears to be for the whole of the networking library). calloutprio - Priority of the SceNetCallout thread. calloutstack - Stack size of the SceNetCallout thread (defaults to 4096 on non 1.5 firmware regardless of what value is passed). netintrprio - Priority of the SceNetNetintr thread. netintrstack - Stack size of the SceNetNetintr thread (defaults to 4096 on non 1.5 firmware regardless of what value is passed). */ static int sceNetInit(u32 poolSize, u32 calloutPri, u32 calloutStack, u32 netinitPri, u32 netinitStack) { // TODO: Create Network Threads using given priority & stack // TODO: The correct behavior is actually to allocate more and leak the other threads/pool. // But we reset here for historic reasons (GTA:VCS potentially triggers this.) if (netInited) Net_Term(); // This cleanup attempt might not worked when SaveState were loaded in the middle of multiplayer game and re-entering multiplayer, thus causing memory leaks & wasting binded ports. May be we shouldn't save/load "Inited" vars on SaveState? if (poolSize == 0) { return hleLogError(SCENET, SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE, "invalid pool size"); } else if (calloutPri < 0x08 || calloutPri > 0x77) { return hleLogError(SCENET, SCE_KERNEL_ERROR_ILLEGAL_PRIORITY, "invalid callout thread priority"); } else if (netinitPri < 0x08 || netinitPri > 0x77) { return hleLogError(SCENET, SCE_KERNEL_ERROR_ILLEGAL_PRIORITY, "invalid init thread priority"); } // TODO: Should also start the threads, probably? For now, let's just allocate. // TODO: Respect the stack size if firmware set to 1.50? u32 stackSize = 4096; netThread1Addr = AllocUser(stackSize, true, "netstack1"); if (netThread1Addr == 0) { return hleLogError(SCENET, SCE_KERNEL_ERROR_NO_MEMORY, "unable to allocate thread"); } netThread2Addr = AllocUser(stackSize, true, "netstack2"); if (netThread2Addr == 0) { FreeUser(netThread1Addr); return hleLogError(SCENET, SCE_KERNEL_ERROR_NO_MEMORY, "unable to allocate thread"); } netPoolAddr = AllocUser(poolSize, false, "netpool"); if (netPoolAddr == 0) { FreeUser(netThread1Addr); FreeUser(netThread2Addr); return hleLogError(SCENET, SCE_KERNEL_ERROR_NO_MEMORY, "unable to allocate pool"); } WARN_LOG(SCENET, "sceNetInit(poolsize=%d, calloutpri=%i, calloutstack=%d, netintrpri=%i, netintrstack=%d) at %08x", poolSize, calloutPri, calloutStack, netinitPri, netinitStack, currentMIPS->pc); 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); } // Free(delete) thread info / data. // Normal usage: sceKernelDeleteThread followed by sceNetFreeThreadInfo with the same threadID as argument static int sceNetFreeThreadinfo(SceUID thid) { ERROR_LOG(SCENET, "UNIMPL sceNetFreeThreadinfo(%i)", thid); return 0; } // Abort a thread. static int sceNetThreadAbort(SceUID thid) { ERROR_LOG(SCENET, "UNIMPL sceNetThreadAbort(%i)", thid); return 0; } static u32 sceWlanGetEtherAddr(u32 addrAddr) { if (!Memory::IsValidRange(addrAddr, 6)) { // More correctly, it should crash. return hleLogError(SCENET, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "illegal address"); } u8 *addr = Memory::GetPointer(addrAddr); if (PPSSPP_ID > 1) { Memory::Memset(addrAddr, PPSSPP_ID, 6); // Making sure the 1st 2-bits on the 1st byte of OUI are zero to prevent issue with some games (ie. Gran Turismo) addr[0] &= 0xfc; } else // Read MAC Address from config if (!ParseMacAddress(g_Config.sMACAddress.c_str(), addr)) { ERROR_LOG(SCENET, "Error parsing mac address %s", g_Config.sMACAddress.c_str()); Memory::Memset(addrAddr, 0, 6); } else { CBreakPoints::ExecMemCheck(addrAddr, true, 6, currentMIPS->pc); } return hleLogSuccessI(SCENET, hleDelayResult(0, "get ether mac", 200)); } static u32 sceNetGetLocalEtherAddr(u32 addrAddr) { // FIXME: Return 0x80410180 (pspnet[_core] error code?) before successful attempt to Create/Connect/Join a Group? (ie. adhocctlCurrentMode == ADHOCCTL_MODE_NONE) if (adhocctlCurrentMode == ADHOCCTL_MODE_NONE) return hleLogDebug(SCENET, 0x80410180, "address not available?"); return sceWlanGetEtherAddr(addrAddr); } static u32 sceWlanDevIsPowerOn() { return hleLogSuccessVerboseI(SCENET, g_Config.bEnableWlan ? 1 : 0); } static u32 sceWlanGetSwitchState() { return hleLogSuccessVerboseI(SCENET, g_Config.bEnableWlan ? 1 : 0); } // Probably a void function, but often returns a useful value. static void sceNetEtherNtostr(u32 macPtr, u32 bufferPtr) { DEBUG_LOG(SCENET, "sceNetEtherNtostr(%08x, %08x) at %08x", macPtr, bufferPtr, currentMIPS->pc); if (Memory::IsValidAddress(bufferPtr) && Memory::IsValidAddress(macPtr)) { char *buffer = (char *)Memory::GetPointer(bufferPtr); const u8 *mac = Memory::GetPointer(macPtr); // MAC address is always 6 bytes / 48 bits. sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); VERBOSE_LOG(SCENET, "sceNetEtherNtostr - [%s]", buffer); } } static int hex_to_digit(int c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } // Probably a void function, but sometimes returns a useful-ish value. static void sceNetEtherStrton(u32 bufferPtr, u32 macPtr) { DEBUG_LOG(SCENET, "sceNetEtherStrton(%08x, %08x)", bufferPtr, macPtr); if (Memory::IsValidAddress(bufferPtr) && Memory::IsValidAddress(macPtr)) { const char *buffer = (char *)Memory::GetPointer(bufferPtr); u8 *mac = Memory::GetPointer(macPtr); // MAC address is always 6 pairs of hex digits. // TODO: Funny stuff happens if it's too short. u8 value = 0; for (int i = 0; i < 6 && *buffer != 0; ++i) { value = 0; int c = hex_to_digit(*buffer++); if (c != -1) { value |= c << 4; } c = hex_to_digit(*buffer++); if (c != -1) { value |= c; } *mac++ = value; // Skip a single character in between. // TODO: Strange behavior on the PSP, let's just null check. if (*buffer++ == 0) { break; } } VERBOSE_LOG(SCENET, "sceNetEtherStrton - [%s]", mac2str((SceNetEtherAddr*)Memory::GetPointer(macPtr)).c_str()); // Seems to maybe kinda return the last value. Probably returns void. //return value; } } // Write static data since we don't actually manage any memory for sceNet* yet. static int sceNetGetMallocStat(u32 statPtr) { VERBOSE_LOG(SCENET, "UNTESTED sceNetGetMallocStat(%x) at %08x", statPtr, currentMIPS->pc); if(Memory::IsValidAddress(statPtr)) Memory::WriteStruct(statPtr, &netMallocStat); else ERROR_LOG(SCENET, "UNTESTED sceNetGetMallocStat(%x): tried to request invalid address!", statPtr); return 0; } static int sceNetInetInit() { ERROR_LOG(SCENET, "UNIMPL sceNetInetInit()"); if (netInetInited) return ERROR_NET_INET_ALREADY_INITIALIZED; netInetInited = true; return 0; } int sceNetInetTerm() { ERROR_LOG(SCENET, "UNIMPL sceNetInetTerm()"); netInetInited = false; return 0; } void NetApctl_InitInfo() { memset(&netApctlInfo, 0, sizeof(netApctlInfo)); // Set dummy/fake values, these probably not suppose to have valid info before connected to an AP, right? std::string APname = "Wifi"; // fake AP/hotspot truncate_cpy(netApctlInfo.name, sizeof(netApctlInfo.name), APname.c_str()); truncate_cpy(netApctlInfo.ssid, sizeof(netApctlInfo.ssid), APname.c_str()); 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); // 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 ((u8*)&sockAddr.sin_addr.s_addr)[3] = 1; inet_ntop(AF_INET, &sockAddr.sin_addr, ipstr, sizeof(ipstr)); truncate_cpy(netApctlInfo.gateway, sizeof(netApctlInfo.gateway), ipstr); truncate_cpy(netApctlInfo.primaryDns, sizeof(netApctlInfo.primaryDns), ipstr); truncate_cpy(netApctlInfo.secondaryDns, sizeof(netApctlInfo.secondaryDns), "8.8.8.8"); truncate_cpy(netApctlInfo.subNetMask, sizeof(netApctlInfo.subNetMask), "255.255.255.0"); } 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; apctlEvents.clear(); netApctlState = PSP_NET_APCTL_STATE_DISCONNECTED; // Set default value before connected to an AP memset(&netApctlInfo, 0, sizeof(netApctlInfo)); // NetApctl_InitInfo(); std::string APname = "Wifi"; // fake AP/hotspot truncate_cpy(netApctlInfo.name, sizeof(netApctlInfo.name), APname.c_str()); truncate_cpy(netApctlInfo.ssid, sizeof(netApctlInfo.ssid), APname.c_str()); memcpy(netApctlInfo.bssid, "\1\1\2\2\3\3", sizeof(netApctlInfo.bssid)); // fake AP's mac address netApctlInfo.ssidLength = static_cast(APname.length()); truncate_cpy(netApctlInfo.ip, sizeof(netApctlInfo.ip), "0.0.0.0"); truncate_cpy(netApctlInfo.gateway, sizeof(netApctlInfo.gateway), "0.0.0.0"); truncate_cpy(netApctlInfo.primaryDns, sizeof(netApctlInfo.primaryDns), "0.0.0.0"); truncate_cpy(netApctlInfo.secondaryDns, sizeof(netApctlInfo.secondaryDns), "0.0.0.0"); truncate_cpy(netApctlInfo.subNetMask, sizeof(netApctlInfo.subNetMask), "0.0.0.0"); // Create APctl fake-Thread netValidateLoopMemory(); apctlThreadID = __KernelCreateThread("ApctlThread", __KernelGetCurThreadModuleId(), apctlThreadHackAddr, initPriority, stackSize, PSP_THREAD_ATTR_USER, 0, true); if (apctlThreadID > 0) { __KernelStartThread(apctlThreadID, 0, 0); } netApctlInited = true; 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() { WARN_LOG(SCENET, "UNTESTED %s()", __FUNCTION__); return NetApctl_Term(); } static int sceNetApctlGetInfo(int code, u32 pInfoAddr) { WARN_LOG(SCENET, "UNTESTED %s(%i, %08x)", __FUNCTION__, code, pInfoAddr); if (!netApctlInited) return hleLogError(SCENET, ERROR_NET_APCTL_NOT_IN_BSS, "apctl not in bss"); // Only have valid info after joining an AP and got an IP, right? if (!Memory::IsValidAddress(pInfoAddr)) return hleLogError(SCENET, -1, "apctl invalid arg"); u8* info = Memory::GetPointer(pInfoAddr); // FIXME: Points to a union instead of a struct thus each field have the same address switch (code) { case PSP_NET_APCTL_INFO_PROFILE_NAME: Memory::WriteStruct(pInfoAddr, &netApctlInfo.name); DEBUG_LOG(SCENET, "ApctlInfo - ProfileName: %s", netApctlInfo.name); break; case PSP_NET_APCTL_INFO_BSSID: Memory::WriteStruct(pInfoAddr, &netApctlInfo.bssid); DEBUG_LOG(SCENET, "ApctlInfo - BSSID: %s", mac2str((SceNetEtherAddr*)&netApctlInfo.bssid).c_str()); break; case PSP_NET_APCTL_INFO_SSID: Memory::WriteStruct(pInfoAddr, &netApctlInfo.ssid); DEBUG_LOG(SCENET, "ApctlInfo - SSID: %s", netApctlInfo.ssid); break; case PSP_NET_APCTL_INFO_SSID_LENGTH: Memory::WriteStruct(pInfoAddr, &netApctlInfo.ssidLength); break; case PSP_NET_APCTL_INFO_SECURITY_TYPE: Memory::WriteStruct(pInfoAddr, &netApctlInfo.securityType); break; case PSP_NET_APCTL_INFO_STRENGTH: Memory::WriteStruct(pInfoAddr, &netApctlInfo.strength); break; case PSP_NET_APCTL_INFO_CHANNEL: Memory::WriteStruct(pInfoAddr, &netApctlInfo.channel); break; case PSP_NET_APCTL_INFO_POWER_SAVE: Memory::WriteStruct(pInfoAddr, &netApctlInfo.powerSave); break; case PSP_NET_APCTL_INFO_IP: Memory::WriteStruct(pInfoAddr, &netApctlInfo.ip); DEBUG_LOG(SCENET, "ApctlInfo - IP: %s", netApctlInfo.ip); break; case PSP_NET_APCTL_INFO_SUBNETMASK: Memory::WriteStruct(pInfoAddr, &netApctlInfo.subNetMask); DEBUG_LOG(SCENET, "ApctlInfo - SubNet Mask: %s", netApctlInfo.subNetMask); break; case PSP_NET_APCTL_INFO_GATEWAY: Memory::WriteStruct(pInfoAddr, &netApctlInfo.gateway); DEBUG_LOG(SCENET, "ApctlInfo - Gateway IP: %s", netApctlInfo.gateway); break; case PSP_NET_APCTL_INFO_PRIMDNS: Memory::WriteStruct(pInfoAddr, &netApctlInfo.primaryDns); DEBUG_LOG(SCENET, "ApctlInfo - Primary DNS: %s", netApctlInfo.primaryDns); break; case PSP_NET_APCTL_INFO_SECDNS: Memory::WriteStruct(pInfoAddr, &netApctlInfo.secondaryDns); DEBUG_LOG(SCENET, "ApctlInfo - Secondary DNS: %s", netApctlInfo.secondaryDns); break; case PSP_NET_APCTL_INFO_USE_PROXY: Memory::WriteStruct(pInfoAddr, &netApctlInfo.useProxy); break; case PSP_NET_APCTL_INFO_PROXY_URL: Memory::WriteStruct(pInfoAddr, &netApctlInfo.proxyUrl); DEBUG_LOG(SCENET, "ApctlInfo - Proxy URL: %s", netApctlInfo.proxyUrl); break; case PSP_NET_APCTL_INFO_PROXY_PORT: Memory::WriteStruct(pInfoAddr, &netApctlInfo.proxyPort); break; case PSP_NET_APCTL_INFO_8021_EAP_TYPE: Memory::WriteStruct(pInfoAddr, &netApctlInfo.eapType); break; case PSP_NET_APCTL_INFO_START_BROWSER: Memory::WriteStruct(pInfoAddr, &netApctlInfo.startBrowser); break; case PSP_NET_APCTL_INFO_WIFISP: Memory::WriteStruct(pInfoAddr, &netApctlInfo.wifisp); break; default: return hleLogError(SCENET, ERROR_NET_APCTL_INVALID_CODE, "apctl invalid code"); } return hleLogSuccessI(SCENET, 0); } int NetApctl_AddHandler(u32 handlerPtr, u32 handlerArg) { bool foundHandler = false; u32 retval = 0; struct ApctlHandler handler; memset(&handler, 0, sizeof(handler)); while (apctlHandlers.find(retval) != apctlHandlers.end()) ++retval; handler.entryPoint = handlerPtr; handler.argument = handlerArg; for (std::map::iterator it = apctlHandlers.begin(); it != apctlHandlers.end(); it++) { if (it->second.entryPoint == handlerPtr) { foundHandler = true; break; } } if (!foundHandler && Memory::IsValidAddress(handlerPtr)) { if (apctlHandlers.size() >= MAX_APCTL_HANDLERS) { ERROR_LOG(SCENET, "Failed to Add handler(%x, %x): Too many handlers", handlerPtr, handlerArg); retval = ERROR_NET_ADHOCCTL_TOO_MANY_HANDLERS; // TODO: What's the proper error code for Apctl's TOO_MANY_HANDLERS? return retval; } apctlHandlers[retval] = handler; WARN_LOG(SCENET, "Added Apctl handler(%x, %x): %d", handlerPtr, handlerArg, retval); } else { ERROR_LOG(SCENET, "Existing Apctl handler(%x, %x)", handlerPtr, handlerArg); } // The id to return is the number of handlers currently registered return retval; } // TODO: How many handlers can the PSP actually have for Apctl? // TODO: Should we allow the same handler to be added more than once? static u32 sceNetApctlAddHandler(u32 handlerPtr, u32 handlerArg) { INFO_LOG(SCENET, "%s(%08x, %08x)", __FUNCTION__, handlerPtr, handlerArg); return NetApctl_AddHandler(handlerPtr, handlerArg); } int NetApctl_DelHandler(u32 handlerID) { if (apctlHandlers.find(handlerID) != apctlHandlers.end()) { apctlHandlers.erase(handlerID); WARN_LOG(SCENET, "Deleted Apctl handler: %d", handlerID); } else { ERROR_LOG(SCENET, "Invalid Apctl handler: %d", handlerID); } return 0; } static int sceNetApctlDelHandler(u32 handlerID) { INFO_LOG(SCENET, "%s(%d)", __FUNCTION__, handlerID); return NetApctl_DelHandler(handlerID); } static int sceNetInetInetAton(const char *hostname, u32 addrPtr) { ERROR_LOG(SCENET, "UNIMPL sceNetInetInetAton(%s, %08x)", hostname, addrPtr); return -1; } int sceNetInetPoll(void *fds, u32 nfds, int timeout) { // timeout in miliseconds DEBUG_LOG(SCENET, "UNTESTED sceNetInetPoll(%p, %d, %i) at %08x", fds, nfds, timeout, currentMIPS->pc); int retval = -1; SceNetInetPollfd *fdarray = (SceNetInetPollfd *)fds; // SceNetInetPollfd/pollfd, sceNetInetPoll() have similarity to BSD poll() but pollfd have different size on 64bit //#ifdef _WIN32 //WSAPoll only available for Vista or newer, so we'll use an alternative way for XP since Windows doesn't have poll function like *NIX if (nfds > FD_SETSIZE) return -1; fd_set readfds, writefds, exceptfds; FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); for (int i = 0; i < (s32)nfds; i++) { if (fdarray[i].events & (INET_POLLRDNORM)) FD_SET(fdarray[i].fd, &readfds); // (POLLRDNORM | POLLIN) if (fdarray[i].events & (INET_POLLWRNORM)) FD_SET(fdarray[i].fd, &writefds); // (POLLWRNORM | POLLOUT) //if (fdarray[i].events & (ADHOC_EV_ALERT)) // (POLLRDBAND | POLLPRI) // POLLERR FD_SET(fdarray[i].fd, &exceptfds); fdarray[i].revents = 0; } timeval tmout; tmout.tv_sec = timeout / 1000; // seconds tmout.tv_usec = (timeout % 1000) * 1000; // microseconds retval = select(nfds, &readfds, &writefds, &exceptfds, &tmout); if (retval < 0) return -1; retval = 0; for (int i = 0; i < (s32)nfds; i++) { if (FD_ISSET(fdarray[i].fd, &readfds)) fdarray[i].revents |= INET_POLLRDNORM; //POLLIN if (FD_ISSET(fdarray[i].fd, &writefds)) fdarray[i].revents |= INET_POLLWRNORM; //POLLOUT fdarray[i].revents &= fdarray[i].events; if (FD_ISSET(fdarray[i].fd, &exceptfds)) fdarray[i].revents |= ADHOC_EV_ALERT; // POLLPRI; // POLLERR; // can be raised on revents regardless of events bitmask? if (fdarray[i].revents) retval++; } //#else /* // Doesn't work properly yet pollfd *fdtmp = (pollfd *)malloc(sizeof(pollfd) * nfds); // Note: sizeof(pollfd) = 16bytes in 64bit and 8bytes in 32bit, while sizeof(SceNetInetPollfd) is always 8bytes for (int i = 0; i < (s32)nfds; i++) { fdtmp[i].fd = fdarray[i].fd; fdtmp[i].events = 0; if (fdarray[i].events & INET_POLLRDNORM) fdtmp[i].events |= (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI); if (fdarray[i].events & INET_POLLWRNORM) fdtmp[i].events |= (POLLOUT | POLLWRNORM | POLLWRBAND); fdtmp[i].revents = 0; fdarray[i].revents = 0; } retval = poll(fdtmp, (nfds_t)nfds, timeout); //retval = WSAPoll(fdarray, nfds, timeout); for (int i = 0; i < (s32)nfds; i++) { if (fdtmp[i].revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) fdarray[i].revents |= INET_POLLRDNORM; if (fdtmp[i].revents & (POLLOUT | POLLWRNORM | POLLWRBAND)) fdarray[i].revents |= INET_POLLWRNORM; fdarray[i].revents &= fdarray[i].events; if (fdtmp[i].revents & POLLERR) fdarray[i].revents |= POLLERR; //INET_POLLERR // can be raised on revents regardless of events bitmask? } free(fdtmp); */ //#endif return retval; } static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, u32 flags) { ERROR_LOG(SCENET, "UNIMPL sceNetInetRecv(%i, %08x, %i, %08x)", socket, bufPtr, bufLen, flags); return -1; } static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) { ERROR_LOG(SCENET, "UNIMPL sceNetInetSend(%i, %08x, %i, %08x)", socket, bufPtr, bufLen, flags); return -1; } static int sceNetInetGetErrno() { ERROR_LOG(SCENET, "UNTESTED sceNetInetGetErrno()"); int error = errno; switch (error) { case ETIMEDOUT: return INET_ETIMEDOUT; case EISCONN: return INET_EISCONN; case EINPROGRESS: return INET_EINPROGRESS; //case EAGAIN: // return INET_EAGAIN; } return error; //-1; } static int sceNetInetSocket(int domain, int type, int protocol) { ERROR_LOG(SCENET, "UNIMPL sceNetInetSocket(%i, %i, %i)", domain, type, protocol); return -1; } static int sceNetInetSetsockopt(int socket, int level, int optname, u32 optvalPtr, int optlen) { ERROR_LOG(SCENET, "UNIMPL sceNetInetSetsockopt(%i, %i, %i, %08x, %i)", socket, level, optname, optvalPtr, optlen); return -1; } static int sceNetInetConnect(int socket, u32 sockAddrInternetPtr, int addressLength) { ERROR_LOG(SCENET, "UNIMPL sceNetInetConnect(%i, %08x, %i)", socket, sockAddrInternetPtr, addressLength); return -1; } int sceNetApctlConnect(int connIndex) { WARN_LOG(SCENET, "UNTESTED %s(%i)", __FUNCTION__, connIndex); // Is this connIndex is the index to the scanning's result data or sceNetApctlGetBSSDescIDListUser result? __UpdateApctlHandlers(0, 0, PSP_NET_APCTL_EVENT_CONNECT_REQUEST, 0); //hleDelayResult(0, "give time to init/cleanup", adhocEventDelayMS * 1000); return 0; } 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). __UpdateApctlHandlers(0, 0, PSP_NET_APCTL_EVENT_DISCONNECT_REQUEST, 0); return 0; } int NetApctl_GetState() { return netApctlState; } static int sceNetApctlGetState(u32 pStateAddr) { //if (!netApctlInited) return hleLogError(SCENET, ERROR_NET_APCTL_NOT_IN_BSS, "apctl not in bss"); // Valid Arguments if (Memory::IsValidAddress(pStateAddr)) { // Return Thread Status Memory::Write_U32(NetApctl_GetState(), pStateAddr); // Return Success return hleLogSuccessI(SCENET, 0); } return hleLogError(SCENET, -1, "apctl invalid arg"); } int NetApctl_ScanUser() { // Scan probably only works when not in connected state, right? if (netApctlState != PSP_NET_APCTL_STATE_DISCONNECTED) return hleLogError(SCENET, ERROR_NET_APCTL_NOT_DISCONNECTED, "apctl not disconnected"); __UpdateApctlHandlers(0, 0, PSP_NET_APCTL_EVENT_SCAN_REQUEST, 0); return 0; } static int sceNetApctlScanUser() { ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); return NetApctl_ScanUser(); } static int sceNetApctlGetBSSDescIDListUser(u32 sizeAddr, u32 bufAddr) { WARN_LOG(SCENET, "UNTESTED %s(%08x, %08x)", __FUNCTION__, sizeAddr, bufAddr); const int userInfoSize = 8; int entries = 1; if (!Memory::IsValidAddress(sizeAddr)) hleLogError(SCENET, -1, "apctl invalid arg"); int size = Memory::Read_U32(sizeAddr); // Return size required Memory::Write_U32(entries * userInfoSize, sizeAddr); if (bufAddr != 0 && Memory::IsValidAddress(sizeAddr)) { int offset = 0; for (int i = 0; i < entries; i++) { // Check if enough space available to write the next structure if (offset + userInfoSize > size) { break; } DEBUG_LOG(SCENET, "%s returning %d at %08x", __FUNCTION__, i, bufAddr + offset); // Pointer to next Network structure in list Memory::Write_U32((i+1)*userInfoSize + bufAddr, bufAddr + offset); offset += 4; // Entry ID Memory::Write_U32(i, bufAddr + offset); offset += 4; } // Fix the last Pointer if (offset > 0) Memory::Write_U32(0, bufAddr + offset - userInfoSize); } return hleLogWarning(SCENET, 0, "untested"); } static int sceNetApctlGetBSSDescEntryUser(int entryId, int infoId, u32 resultAddr) { WARN_LOG(SCENET, "UNTESTED %s(%i, %i, %08x)", __FUNCTION__, entryId, infoId, resultAddr); if (!Memory::IsValidAddress(resultAddr)) hleLogError(SCENET, -1, "apctl invalid arg"); switch (infoId) { case PSP_NET_APCTL_DESC_IBSS: // IBSS, 6 bytes Memory::WriteStruct(resultAddr, &netApctlInfo.bssid); break; case PSP_NET_APCTL_DESC_SSID_NAME: // Return 32 bytes Memory::WriteStruct(resultAddr, &netApctlInfo.ssid); break; case PSP_NET_APCTL_DESC_SSID_NAME_LENGTH: // Return one 32-bit value Memory::WriteStruct(resultAddr, &netApctlInfo.ssidLength); break; case PSP_NET_APCTL_DESC_SIGNAL_STRENGTH: // Return 1 byte Memory::WriteStruct(resultAddr, &netApctlInfo.strength); break; case PSP_NET_APCTL_DESC_SECURITY: // Return one 32-bit value Memory::WriteStruct(resultAddr, &netApctlInfo.securityType); break; default: return hleLogError(SCENET, ERROR_NET_APCTL_INVALID_CODE, "unknown info id"); } return hleLogWarning(SCENET, 0, "untested"); } static int sceNetApctlScanSSID2() { ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); return NetApctl_ScanUser(); } static int sceNetApctlGetBSSDescIDList2(u32 Arg1, u32 Arg2, u32 Arg3, u32 Arg4) { return hleLogError(SCENET, 0, "unimplemented"); } static int sceNetApctlGetBSSDescEntry2(u32 Arg1, u32 Arg2, u32 Arg3, u32 Arg4) { return hleLogError(SCENET, 0, "unimplemented"); } static int sceNetResolverInit() { ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); return 0; } static int sceNetApctlAddInternalHandler(u32 handlerPtr, u32 handlerArg) { ERROR_LOG(SCENET, "UNIMPL %s(%08x, %08x)", __FUNCTION__, handlerPtr, handlerArg); // This seems to be a 2nd kind of handler return NetApctl_AddHandler(handlerPtr, handlerArg); } static int sceNetApctlDelInternalHandler(u32 handlerID) { ERROR_LOG(SCENET, "UNIMPL %s(%i)", __FUNCTION__, handlerID); // This seems to be a 2nd kind of handler return NetApctl_DelHandler(handlerID); } static int sceNetApctl_A7BB73DF(u32 handlerPtr, u32 handlerArg) { ERROR_LOG(SCENET, "UNIMPL %s(%08x, %08x)", __FUNCTION__, handlerPtr, handlerArg); // This seems to be a 3rd kind of handler return sceNetApctlAddHandler(handlerPtr, handlerArg); } static int sceNetApctl_6F5D2981(u32 handlerID) { ERROR_LOG(SCENET, "UNIMPL %s(%i)", __FUNCTION__, handlerID); // This seems to be a 3rd kind of handler return sceNetApctlDelHandler(handlerID); } static int sceNetApctl_lib2_69745F0A(int handlerId) { return hleLogError(SCENET, 0, "unimplemented"); } static int sceNetApctl_lib2_4C19731F(int code, u32 pInfoAddr) { ERROR_LOG(SCENET, "UNIMPL %s(%i, %08x)", __FUNCTION__, code, pInfoAddr); return sceNetApctlGetInfo(code, pInfoAddr); } static int sceNetApctlScan() { ERROR_LOG(SCENET, "UNIMPL %s()", __FUNCTION__); return NetApctl_ScanUser(); } static int sceNetApctlGetBSSDescIDList(u32 sizeAddr, u32 bufAddr) { ERROR_LOG(SCENET, "UNIMPL %s(%08x, %08x)", __FUNCTION__, sizeAddr, bufAddr); return sceNetApctlGetBSSDescIDListUser(sizeAddr, bufAddr); } static int sceNetApctlGetBSSDescEntry(int entryId, int infoId, u32 resultAddr) { ERROR_LOG(SCENET, "UNIMPL %s(%i, %i, %08x)", __FUNCTION__, entryId, infoId, resultAddr); return sceNetApctlGetBSSDescEntryUser(entryId, infoId, resultAddr); } static int sceNetApctl_lib2_C20A144C(int connIndex, u32 ps3MacAddressPtr) { ERROR_LOG(SCENET, "UNIMPL %s(%i, %08x)", __FUNCTION__, connIndex, ps3MacAddressPtr); return sceNetApctlConnect(connIndex); } static int sceNetUpnpInit(int unknown1,int unknown2) { ERROR_LOG_REPORT_ONCE(sceNetUpnpInit, SCENET, "UNIMPLsceNetUpnpInit %d,%d",unknown1,unknown2); return 0; } static int sceNetUpnpStart() { ERROR_LOG(SCENET, "UNIMPLsceNetUpnpStart"); return 0; } static int sceNetUpnpStop() { ERROR_LOG(SCENET, "UNIMPLsceNetUpnpStop"); return 0; } static int sceNetUpnpTerm() { ERROR_LOG(SCENET, "UNIMPLsceNetUpnpTerm"); return 0; } static int sceNetUpnpGetNatInfo() { ERROR_LOG(SCENET, "UNIMPLsceNetUpnpGetNatInfo"); return 0; } static int sceNetGetDropRate(u32 dropRateAddr, u32 dropDurationAddr) { Memory::Write_U32(netDropRate, dropRateAddr); Memory::Write_U32(netDropDuration, dropDurationAddr); return hleLogSuccessInfoI(SCENET, 0); } static int sceNetSetDropRate(u32 dropRate, u32 dropDuration) { netDropRate = dropRate; netDropDuration = dropDuration; return hleLogSuccessInfoI(SCENET, 0); } const HLEFunction sceNet[] = { {0X39AF39A6, &WrapI_UUUUU, "sceNetInit", 'i', "xxxxx"}, {0X281928A9, &WrapU_V, "sceNetTerm", 'x', "" }, {0X89360950, &WrapV_UU, "sceNetEtherNtostr", 'v', "xx" }, {0XD27961C9, &WrapV_UU, "sceNetEtherStrton", 'v', "xx" }, {0X0BF0A3AE, &WrapU_U, "sceNetGetLocalEtherAddr", 'x', "x" }, {0X50647530, &WrapI_I, "sceNetFreeThreadinfo", 'i', "i" }, {0XCC393E48, &WrapI_U, "sceNetGetMallocStat", 'i', "x" }, {0XAD6844C6, &WrapI_I, "sceNetThreadAbort", 'i', "i" }, }; const HLEFunction sceNetResolver[] = { {0X224C5F44, nullptr, "sceNetResolverStartNtoA", '?', "" }, {0X244172AF, nullptr, "sceNetResolverCreate", '?', "" }, {0X94523E09, nullptr, "sceNetResolverDelete", '?', "" }, {0XF3370E61, &WrapI_V, "sceNetResolverInit", 'i', "" }, {0X808F6063, nullptr, "sceNetResolverStop", '?', "" }, {0X6138194A, nullptr, "sceNetResolverTerm", '?', "" }, {0X629E2FB7, nullptr, "sceNetResolverStartAtoN", '?', "" }, {0X14C17EF9, nullptr, "sceNetResolverStartNtoAAsync", '?', "" }, {0XAAC09184, nullptr, "sceNetResolverStartAtoNAsync", '?', "" }, {0X12748EB9, nullptr, "sceNetResolverWaitAsync", '?', "" }, {0X4EE99358, nullptr, "sceNetResolverPollAsync", '?', "" }, }; const HLEFunction sceNetInet[] = { {0X17943399, &WrapI_V, "sceNetInetInit", 'i', "" }, {0X4CFE4E56, nullptr, "sceNetInetShutdown", '?', "" }, {0XA9ED66B9, &WrapI_V, "sceNetInetTerm", 'i', "" }, {0X8B7B220F, &WrapI_III, "sceNetInetSocket", 'i', "iii" }, {0X2FE71FE7, &WrapI_IIIUI, "sceNetInetSetsockopt", 'i', "iiixi"}, {0X4A114C7C, nullptr, "sceNetInetGetsockopt", '?', "" }, {0X410B34AA, &WrapI_IUI, "sceNetInetConnect", 'i', "ixi" }, {0X805502DD, nullptr, "sceNetInetCloseWithRST", '?', "" }, {0XD10A1A7A, nullptr, "sceNetInetListen", '?', "" }, {0XDB094E1B, nullptr, "sceNetInetAccept", '?', "" }, {0XFAABB1DD, &WrapI_VUI, "sceNetInetPoll", 'i', "pxi" }, {0X5BE8D595, nullptr, "sceNetInetSelect", '?', "" }, {0X8D7284EA, nullptr, "sceNetInetClose", '?', "" }, {0XCDA85C99, &WrapI_IUUU, "sceNetInetRecv", 'i', "ixxx" }, {0XC91142E4, nullptr, "sceNetInetRecvfrom", '?', "" }, {0XEECE61D2, nullptr, "sceNetInetRecvmsg", '?', "" }, {0X7AA671BC, &WrapI_IUUU, "sceNetInetSend", 'i', "ixxx" }, {0X05038FC7, nullptr, "sceNetInetSendto", '?', "" }, {0X774E36F4, nullptr, "sceNetInetSendmsg", '?', "" }, {0XFBABE411, &WrapI_V, "sceNetInetGetErrno", 'i', "" }, {0X1A33F9AE, nullptr, "sceNetInetBind", '?', "" }, {0XB75D5B0A, nullptr, "sceNetInetInetAddr", '?', "" }, {0X1BDF5D13, &WrapI_CU, "sceNetInetInetAton", 'i', "sx" }, {0XD0792666, nullptr, "sceNetInetInetNtop", '?', "" }, {0XE30B8C19, nullptr, "sceNetInetInetPton", '?', "" }, {0X8CA3A97E, nullptr, "sceNetInetGetPspError", '?', "" }, {0XE247B6D6, nullptr, "sceNetInetGetpeername", '?', "" }, {0X162E6FD5, nullptr, "sceNetInetGetsockname", '?', "" }, {0X80A21ABD, nullptr, "sceNetInetSocketAbort", '?', "" }, {0X39B0C7D3, nullptr, "sceNetInetGetUdpcbstat", '?', "" }, {0XB3888AD4, nullptr, "sceNetInetGetTcpcbstat", '?', "" }, }; const HLEFunction sceNetApctl[] = { {0XCFB957C6, &WrapI_I, "sceNetApctlConnect", 'i', "i" }, {0X24FE91A1, &WrapI_V, "sceNetApctlDisconnect", 'i', "" }, {0X5DEAC81B, &WrapI_U, "sceNetApctlGetState", 'i', "x" }, {0X8ABADD51, &WrapU_UU, "sceNetApctlAddHandler", 'x', "xx" }, {0XE2F91F9B, &WrapI_II, "sceNetApctlInit", 'i', "ii" }, {0X5963991B, &WrapI_U, "sceNetApctlDelHandler", 'i', "x" }, {0XB3EDD0EC, &WrapI_V, "sceNetApctlTerm", 'i', "" }, {0X2BEFDF23, &WrapI_IU, "sceNetApctlGetInfo", 'i', "ix" }, {0XA3E77E13, &WrapI_V, "sceNetApctlScanSSID2", 'i', "" }, {0XE9B2E5E6, &WrapI_V, "sceNetApctlScanUser", 'i', "" }, {0XF25A5006, &WrapI_UUUU, "sceNetApctlGetBSSDescIDList2", 'i', "xxxx" }, {0X2935C45B, &WrapI_UUUU, "sceNetApctlGetBSSDescEntry2", 'i', "xxxx" }, {0X04776994, &WrapI_IIU, "sceNetApctlGetBSSDescEntryUser", 'i', "iix" }, {0X6BDDCB8C, &WrapI_UU, "sceNetApctlGetBSSDescIDListUser", 'i', "xx" }, {0X7CFAB990, &WrapI_UU, "sceNetApctlAddInternalHandler", 'i', "xx" }, {0XE11BAFAB, &WrapI_U, "sceNetApctlDelInternalHandler", 'i', "x" }, {0XA7BB73DF, &WrapI_UU, "sceNetApctl_A7BB73DF", 'i', "xx" }, {0X6F5D2981, &WrapI_U, "sceNetApctl_6F5D2981", 'i', "x" }, {0X69745F0A, &WrapI_I, "sceNetApctl_lib2_69745F0A", 'i', "i" }, {0X4C19731F, &WrapI_IU, "sceNetApctl_lib2_4C19731F", 'i', "ix" }, {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" }, // Fake function for PPSSPP's use. {0X756E6F10, &WrapV_V<__NetApctlCallbacks>, "__NetApctlCallbacks", 'v', "" }, }; const HLEFunction sceWlanDrv[] = { {0XD7763699, &WrapU_V, "sceWlanGetSwitchState", 'x', "" }, {0X0C622081, &WrapU_U, "sceWlanGetEtherAddr", 'x', "x" }, {0X93440B11, &WrapU_V, "sceWlanDevIsPowerOn", 'x', "" }, }; // see http://www.kingx.de/forum/showthread.php?tid=35164 const HLEFunction sceNetUpnp[] = { {0X27045362, &WrapI_V, "sceNetUpnpGetNatInfo", 'i', "" }, {0X3432B2E5, &WrapI_V, "sceNetUpnpStart", 'i', "" }, {0X3E32ED9E, &WrapI_V, "sceNetUpnpStop", 'i', "" }, {0X540491EF, &WrapI_V, "sceNetUpnpTerm", 'i', "" }, {0XE24220B5, &WrapI_II, "sceNetUpnpInit", 'i', "ii" }, }; const HLEFunction sceNetIfhandle[] = { {0xC80181A2, &WrapI_UU, "sceNetGetDropRate", 'i', "pp" }, {0xFD8585E1, &WrapI_UU, "sceNetSetDropRate", 'i', "ii" }, }; void Register_sceNet() { RegisterModule("sceNet", ARRAY_SIZE(sceNet), sceNet); RegisterModule("sceNetResolver", ARRAY_SIZE(sceNetResolver), sceNetResolver); RegisterModule("sceNetInet", ARRAY_SIZE(sceNetInet), sceNetInet); RegisterModule("sceNetApctl", ARRAY_SIZE(sceNetApctl), sceNetApctl); } void Register_sceWlanDrv() { RegisterModule("sceWlanDrv", ARRAY_SIZE(sceWlanDrv), sceWlanDrv); } void Register_sceNetUpnp() { RegisterModule("sceNetUpnp", ARRAY_SIZE(sceNetUpnp), sceNetUpnp); } void Register_sceNetIfhandle() { RegisterModule("sceNetIfhandle", ARRAY_SIZE(sceNetIfhandle), sceNetIfhandle); }