Merge pull request #13216 from ANR2ME/net_apctl_stuff

Added a fake thread to process Apctl Request Events into Apctl State Changes
This commit is contained in:
Henrik Rydgård 2020-08-02 11:05:17 +02:00 committed by GitHub
commit 914b6ce24c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 381 additions and 164 deletions

View file

@ -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);

View file

@ -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<int, ApctlHandler> 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<ApctlArgs> 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(&parameter, 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<std::recursive_mutex> apctlGuard(apctlEvtMtx);
apctlEvents.push_back({ oldState, newState, flag, error });
}
for(std::map<int, ApctlHandler>::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<std::recursive_mutex> 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<int, ApctlHandler>::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<unsigned int>(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>, "sceNetApctlScan", 'i', "" },
{0X0C7FFA5C, &WrapI_UU<sceNetApctlGetBSSDescIDList>, "sceNetApctlGetBSSDescIDList", 'i', "xx" },
{0X96BEB231, &WrapI_IIU<sceNetApctlGetBSSDescEntry>, "sceNetApctlGetBSSDescEntry", 'i', "iix" },
{0XC20A144C, &WrapI_IU<sceNetApctl_lib2_C20A144C>, "sceNetApctl_lib2_C20A144C", 'i', "ix" },
{0XC20A144C, &WrapI_IU<sceNetApctl_lib2_C20A144C>, "sceNetApctl_lib2_C20A144C", 'i', "ix" },
// Fake function for PPSSPP's use.
{0X756E6F10, &WrapV_V<__NetApctlCallbacks>, "__NetApctlCallbacks", 'v', "" },
};
const HLEFunction sceWlanDrv[] = {

View file

@ -20,6 +20,14 @@
#include <StringUtils.h>
#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 <typename I> 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();

View file

@ -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<std::pair<u32, u32>> adhocctlEvents;
std::deque<MatchingArgs> matchingEvents;
std::map<int, AdhocctlHandler> adhocctlHandlers;
std::vector<SceUID> 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<int>(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<int, AdhocctlHandler>::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]);

View file

@ -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();