ppsspp/Core/HLE/sceNetAdhoc.cpp
2015-01-04 13:04:06 +01:00

5065 lines
157 KiB
C++

// Copyright (c) 2013- 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/.
// sceNetAdhoc
// This is a direct port of Coldbird's code from http://code.google.com/p/aemu/
// All credit goes to him!
#include "Core/Core.h"
#include "Core/MemMap.h"
#include "Common/ChunkFile.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/HLE/HLEHelperThread.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/HLE/sceKernelThread.h"
#include "Core/HLE/sceNetAdhoc.h"
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/HLE/sceKernelModule.h"
#include "Core/HLE/sceKernelInterrupt.h"
#include "Core/HLE/proAdhoc.h"
#include "Core/HLE/sceNet.h"
#include "Core/HLE/proAdhocServer.h"
// shared in sceNetAdhoc.h since it need to be used from sceNet.cpp also
bool netAdhocInited;
bool netAdhocctlInited;
static bool netAdhocMatchingInited;
int netAdhocMatchingStarted = 0;
SceUID threadAdhocID;
Thread *threadAdhoc;
u32 dummyThreadHackAddr;
std::map<int, AdhocctlHandler> adhocctlHandlers;
int matchingEventThread(int matchingId);
int matchingInputThread(int matchingId);
int sceNetAdhocTerm();
int sceNetAdhocctlTerm();
int sceNetAdhocMatchingTerm();
int sceNetAdhocMatchingSetHelloOpt(int matchingId, int optLenAddr, u32 optDataAddr);
static u32_le dummyThreadCode[4]; /* = {
MIPS_MAKE_ADDIU(MIPS_REG_A0, MIPS_REG_ZERO, 1000),
MIPS_MAKE_SYSCALL("ThreadManForUser", "sceKernelDelayThread"),
MIPS_MAKE_B(-3),
MIPS_MAKE_NOP(),
//MIPS_MAKE_BREAK(0),
};*/
void __NetAdhocShutdown() {
// Checks to avoid confusing logspam
if (netAdhocMatchingInited) {
sceNetAdhocMatchingTerm();
}
if (netAdhocctlInited) {
sceNetAdhocctlTerm();
}
if (netAdhocInited) {
sceNetAdhocTerm();
}
kernelMemory.Free(dummyThreadHackAddr);
}
void __NetAdhocDoState(PointerWrap &p) {
auto s = p.Section("sceNetAdhoc", 1);
if (!s)
return;
p.Do(netAdhocInited);
p.Do(netAdhocctlInited);
p.Do(netAdhocMatchingInited);
p.Do(adhocctlHandlers);
//p.Do(actionAfterMatchingMipsCall); // This could cause incompatibility with old savestate right?
//__KernelRestoreActionType(actionAfterMatchingMipsCall, AfterMatchingMipsCall::Create);
//u32 blockSize = sizeof(dummyThreadCode);
//dummyThreadHackAddr = kernelMemory.Alloc(blockSize, false, "dummythreadhack");
Memory::Memcpy(dummyThreadHackAddr, dummyThreadCode, sizeof(dummyThreadCode));
}
static void __UpdateAdhocctlHandlers(int flag, int error) {
u32_le args[3] = { 0, 0, 0 };
args[0] = flag;
args[1] = error;
//__KernelSwitchToThread(threadIdleID[1], "switch to Adhocctl");
for (std::map<int, AdhocctlHandler>::iterator it = adhocctlHandlers.begin(); it != adhocctlHandlers.end(); ++it) {
args[2] = it->second.argument;
//__KernelDirectMipsCall(it->second.entryPoint, NULL, args, 3, true);
__KernelCallAddress(threadAdhoc, it->second.entryPoint, NULL, args, 3, true, 0);
}
}
// TODO: MipsCall needs to be called from it's own PSP Thread instead of from any random PSP Thread
void __UpdateMatchingHandler(u64 ArgsPtr) {
u32_le *args = (u32_le *)ArgsPtr;
AfterMatchingMipsCall *after = (AfterMatchingMipsCall *)__KernelCreateAction(actionAfterMatchingMipsCall);
after->SetContextID(args[0], args[1]);
//u32 error;
//Thread *t = kernelObjects.Get<Thread>(threadAdhocID, error);
//__KernelSwitchToThread(threadIdleID[1], "switch to AdhocMatching"); // __KernelSwitchContext(threadIdleID[1], "switch to AdhocMatching");
__KernelCallAddress(threadAdhoc, args[5], after, args, 5, true, 0);
//__KernelDirectMipsCall(args[5], after, args, 5, true);
// Make sure MIPS call have been fully executed before the next callback invoked
//while (/*(after != NULL) &&*/ IsMatchingInCallback(context)) sleep_ms(1); //Must not sleep inside callback handler otherwise Metal Slug XX will gets ThreadQueueList Empty popup
}
static int getBlockingFlag(int id) {
#ifdef _MSC_VER
return 0;
#else
int sockflag = fcntl(id, F_GETFL, O_NONBLOCK);
return sockflag & O_NONBLOCK;
#endif
}
static void __handlerAdhocctlUpdateCallback(u64 userdata, int cycleslate) {
//SceUID cur = __KernelGetCurThread();
// TODO: Since we only have access to __KernelDirectMipsCall we need to switch the thread context first to make sure the mipscall executed on the right thread
// Since __KernelSwitchToThread will flood the log with errors when current thread is already non-idle thread so we'll try to use any "fake" psp thread, BUT we need to backup ALL registers before the mipscall to prevent registers corruption after returning from mipscall
if ((__IsInInterrupt() || !__KernelIsDispatchEnabled() || __KernelInCallback()) /*||*/)
// ((cur != threadAdhocID && cur != threadIdleID[0] && cur != threadIdleID[1]) /*&& !__KernelSwitchToThread(threadAdhocID, "switch to AdhocThread")*/))
{
// // Can't do anything now, inside an interrupt. Let's wait until later.
CoreTiming::ScheduleEvent(100, eventAdhocctlHandlerUpdate, userdata);
return;
}
int buff[2];
split64(userdata,buff);
__UpdateAdhocctlHandlers(buff[0], buff[1]);
}
static void __handlerMatchingUpdateCallback(u64 userdata, int cycleslate) {
//SceUID cur = __KernelGetCurThread();
// TODO: Since we only have access to __KernelDirectMipsCall we need to switch the thread context first to make sure the mipscall executed on the right thread
// Since __KernelSwitchToThread will flood the log with errors when current thread is already non-idle thread so we'll try to use any "fake" psp thread, BUT we need to backup ALL registers before the mipscall to prevent registers corruption after returning from mipscall
if ((__IsInInterrupt() || !__KernelIsDispatchEnabled() || __KernelInCallback()) /*||*/)
// ((cur != threadAdhocID && cur != threadIdleID[0] && cur != threadIdleID[1]) /*&& !__KernelSwitchToThread(threadAdhocID, "switch to AdhocThread")*/))
{
// Can't do anything now, inside an interrupt. Let's wait until later.
CoreTiming::ScheduleEvent(100, eventMatchingHandlerUpdate, userdata);
return;
}
__UpdateMatchingHandler(userdata);
}
void __NetAdhocInit() {
friendFinderRunning = false;
netAdhocInited = false;
netAdhocctlInited = false;
netAdhocMatchingInited = false;
adhocctlHandlers.clear();
__AdhocServerInit();
dummyThreadCode[0] = MIPS_MAKE_ADDIU(MIPS_REG_A0, MIPS_REG_ZERO, 1000);
dummyThreadCode[1] = MIPS_MAKE_SYSCALL("ThreadManForUser", "sceKernelDelayThread");
dummyThreadCode[2] = MIPS_MAKE_B(-3);
dummyThreadCode[3] = MIPS_MAKE_NOP();
//dummyThreadCode[4] = MIPS_MAKE_BREAK(0);
u32 blockSize = sizeof(dummyThreadCode);
dummyThreadHackAddr = kernelMemory.Alloc(blockSize, false, "dummythreadhack");
Memory::Memcpy(dummyThreadHackAddr, dummyThreadCode, sizeof(dummyThreadCode)); // This area will be cleared again after loading an old savestate :(
actionAfterMatchingMipsCall = __KernelRegisterActionType(AfterMatchingMipsCall::Create);
eventAdhocctlHandlerUpdate = CoreTiming::RegisterEvent("AdhocctlHandlerUpdateEvent", __handlerAdhocctlUpdateCallback);
eventMatchingHandlerUpdate = CoreTiming::RegisterEvent("MatchingHandlerUpdateEvent", __handlerMatchingUpdateCallback);
}
u32 sceNetAdhocInit() {
// Library uninitialized
INFO_LOG(SCENET, "sceNetAdhocInit() at %08x", currentMIPS->pc);
if (!netAdhocInited) {
// Clear Translator Memory
memset(&pdp, 0, sizeof(pdp));
memset(&ptp, 0, sizeof(ptp));
// Library initialized
netAdhocInited = true;
// Create fake PSP Thread for callback
// TODO: Should use a separated threads for friendFinder, matchingEvent, and matchingInput and created on AdhocctlInit & AdhocMatchingStart instead of here
#define PSP_THREAD_ATTR_KERNEL 0x00001000 // PSP_THREAD_ATTR_KERNEL is located in sceKernelThread.cpp instead of sceKernelThread.h :(
//threadAdhocID = __KernelCreateThreadInternal("AdhocThread", __KernelGetCurThreadModuleId(), dummyThreadHackAddr, 0x30, 4096, PSP_THREAD_ATTR_KERNEL);
threadAdhoc = __KernelCreateThread(threadAdhocID, __KernelGetCurThreadModuleId(), "AdhocThread", dummyThreadHackAddr, 0x10, 8192, PSP_THREAD_ATTR_KERNEL); // We don't have access to __KernelCreateThread function that use Thread class from sceKernelThread.h :(
if (threadAdhocID > 0) {
__KernelStartThread(threadAdhocID, 0, 0);
}
// Create built-in AdhocServer Thread
if (g_Config.bEnableWlan && g_Config.bEnableAdhocServer) {
//_status = 1;
adhocServerRunning = true;
adhocServerThread = std::thread(proAdhocServerThread, SERVER_PORT);
}
// Return Success
return 0;
}
// Already initialized
return ERROR_NET_ADHOC_ALREADY_INITIALIZED;
}
static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) {
INFO_LOG(SCENET, "sceNetAdhocctlInit(%i, %i, %08x) at %08x", stackSize, prio, productAddr, currentMIPS->pc);
/*if (!g_Config.bEnableWlan) {
// Pretend success but don't actually start the friendfinder thread and stuff.
// Dunno if this is the way to go...
netAdhocctlInited = true;
return 0; //Faking success to prevent GTA:VCS stuck in Host/Join screen
}*/
if (netAdhocctlInited) return ERROR_NET_ADHOCCTL_ALREADY_INITIALIZED;
if (initNetwork((SceNetAdhocctlAdhocId *)Memory::GetPointer(productAddr)) != 0) WARN_LOG(SCENET, "sceNetAdhocctlInit: Faking success");
if (!friendFinderRunning) {
friendFinderRunning = true;
friendFinderThread = std::thread(friendFinder);
}
netAdhocctlInited = true; //needed for cleanup during AdhocctlTerm even when it failed to connect to Adhoc Server (since it's being faked as success)
return 0;
}
static int sceNetAdhocctlGetState(u32 ptrToStatus) {
// Library initialized
if (netAdhocctlInited) {
// Valid Arguments
if (Memory::IsValidAddress(ptrToStatus)) {
// Return Thread Status
Memory::Write_U32(threadStatus, ptrToStatus);
// Return Success
return 0;
}
// Invalid Arguments
return ERROR_NET_ADHOCCTL_INVALID_ARG;
}
// Library uninitialized
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
}
/**
* Adhoc Emulator PDP Socket Creator
* @param saddr Local MAC (Unused)
* @param sport Local Binding Port
* @param bufsize Socket Buffer Size
* @param flag Bitflags (Unused)
* @return Socket ID > 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_SOCKET_ID_NOT_AVAIL, ADHOC_INVALID_ADDR, ADHOC_PORT_NOT_AVAIL, ADHOC_INVALID_PORT, ADHOC_PORT_IN_USE, NET_NO_SPACE
*/
// When choosing AdHoc menu in Wipeout Pulse sometimes it's saying that "WLAN is turned off" on game screen and getting "kUnityCommandCode_MediaDisconnected" error in the Log Console when calling sceNetAdhocPdpCreate, probably it needed to wait something from the thread before calling this (ie. need to receives 7 bytes from adhoc server 1st?)
static int sceNetAdhocPdpCreate(const char *mac, u32 port, int bufferSize, u32 unknown) {
INFO_LOG(SCENET, "sceNetAdhocPdpCreate(%s, %u, %u, %u) at %08x", mac, port, bufferSize, unknown, currentMIPS->pc);
if (!g_Config.bEnableWlan) {
return -1;
}
int retval = ERROR_NET_ADHOC_NOT_INITIALIZED;
// Library is initialized
SceNetEtherAddr * saddr = (SceNetEtherAddr *)mac;
if (netAdhocInited) {
// Valid Arguments are supplied
if (mac != NULL && bufferSize > 0) {
// Valid MAC supplied
if (isLocalMAC(saddr)) {
//// Unused Port supplied
//if (!_IsPDPPortInUse(port)) {}
//
//// Port is in use by another PDP Socket
//return ERROR_NET_ADHOC_PORT_IN_USE;
// Create Internet UDP Socket
int usocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// Valid Socket produced
if (usocket != INVALID_SOCKET) {
// Change socket buffer size when necessary
if (getSockBufferSize(usocket, SO_SNDBUF) < bufferSize) setSockBufferSize(usocket, SO_SNDBUF, bufferSize);
if (getSockBufferSize(usocket, SO_RCVBUF) < bufferSize) setSockBufferSize(usocket, SO_RCVBUF, bufferSize);
// Enable Port Re-use, this will allow binding to an already used port, but only one of them can read the data (shared receive buffer?)
int one = 1;
setsockopt(usocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&one, sizeof(one)); // NO idea if we need this
// Binding Information for local Port
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
//if (port < 7) addr.sin_port = htons(port + 1341); else // <= 443
addr.sin_port = htons(port); // This not safe in any way...
// The port might be under 1024 (ie. GTA:VCS use port 1, Ford Street Racing use port 0 (UNUSED_PORT), etc) and already used by other application/host OS, should we add 1024 to the port whenever it tried to use an already used port?
// Bound Socket to local Port
int iResult = bind(usocket, (sockaddr *)&addr, sizeof(addr));
/*if (iResult == SOCKET_ERROR && errno == 10048) { //Forcing PdpCreate to be successfull using a different Port might not works and might affect other players sharing the same IP due to "deleteFriendByIP" (should delete by MAC instead of IP)
addr.sin_port = 0; //UNUSED_PORT
iResult = bind(usocket, (sockaddr *)&addr, sizeof(addr));
WARN_LOG(SCENET, "Port %u is already used, replaced with UNUSED_PORT(%u)", port, ntohs(addr.sin_port));
}*/
if (iResult == 0) {
// Allocate Memory for Internal Data
SceNetAdhocPdpStat * internal = (SceNetAdhocPdpStat *)malloc(sizeof(SceNetAdhocPdpStat));
// Allocated Memory
if (internal != NULL) {
// Clear Memory
memset(internal, 0, sizeof(SceNetAdhocPdpStat));
// Find Free Translator Index
int i = 0; for (; i < 255; i++) if (pdp[i] == NULL) break;
// Found Free Translator Index
if (i < 255) {
// Fill in Data
internal->id = usocket;
internal->laddr = *saddr;
internal->lport = getLocalPort(usocket); //should use the port given to the socket (in case it's UNUSED_PORT port) isn't?
internal->rcv_sb_cc = bufferSize;
// Link Socket to Translator ID
pdp[i] = internal;
// Forward Port on Router
//sceNetPortOpen("UDP", sport); // I need to figure out how to use this in windows/linux
// Success
return i + 1;
}
// Free Memory for Internal Data
free(internal);
}
retval = ERROR_NET_NO_SPACE;
}
else {
retval = ERROR_NET_ADHOC_PORT_IN_USE;
if (iResult == SOCKET_ERROR) {
ERROR_LOG(SCENET, "Socket error (%i) when binding port %u", errno, ntohs(addr.sin_port));
}
}
// Close Socket
closesocket(usocket);
return retval;
}
// Default to No-Space Error
return ERROR_NET_NO_SPACE;
}
// Invalid MAC supplied
//return ERROR_NET_ADHOC_INVALID_ADDR;
}
// Invalid Arguments were supplied
return ERROR_NET_ADHOC_INVALID_ARG;
}
// Library is uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
/**
* Get Adhoc Parameter
* @param parameter OUT: Adhoc Parameter
* @return 0 on success or... ADHOCCTL_NOT_INITIALIZED, ADHOCCTL_INVALID_ARG
*/
static int sceNetAdhocctlGetParameter(u32 paramAddr) {
DEBUG_LOG(SCENET, "sceNetAdhocctlGetParameter(%08x)",paramAddr);
if (!g_Config.bEnableWlan) {
return ERROR_NET_ADHOCCTL_DISCONNECTED;
}
// Library initialized
if (netAdhocctlInited) {
// Valid Arguments
if (Memory::IsValidAddress(paramAddr)) {
// Copy Parameter
Memory::WriteStruct(paramAddr,&parameter);
// Return Success
return 0;
}
// Invalid Arguments
return ERROR_NET_ADHOCCTL_INVALID_ARG;
}
// Library uninitialized
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
}
/**
* Adhoc Emulator PDP Send Call
* @param id Socket File Descriptor
* @param daddr Target MAC Address
* @param dport Target Port
* @param data Data Payload
* @param len Payload Length
* @param timeout Send Timeout
* @param flag Nonblocking Flag
* @return 0 on success or... ADHOC_INVALID_ARG, ADHOC_NOT_INITIALIZED, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_INVALID_ADDR, ADHOC_INVALID_PORT, ADHOC_INVALID_DATALEN, ADHOC_SOCKET_ALERTED, ADHOC_TIMEOUT, ADHOC_THREAD_ABORTED, ADHOC_WOULD_BLOCK, NET_NO_SPACE, NET_INTERNAL
*/
static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int len, int timeout, int flag) {
DEBUG_LOG(SCENET, "sceNetAdhocPdpSend(%i, %s, %i, %p, %i, %i, %i)", id, mac, port, data, len, timeout, flag);
if (!g_Config.bEnableWlan) {
return -1;
}
SceNetEtherAddr * daddr = (SceNetEtherAddr *)mac;
uint16 dport = (uint16)port;
//if (dport < 7) dport += 1341;
// Really should flatten this with early outs, all this indentation is making me dizzy.
// Library is initialized
if (netAdhocInited) {
// Valid Port
if (dport != 0) {
// Valid Data Length
if (len >= 0) { // should we allow 0 size packet (for ping) ?
// Valid Socket ID
if (id > 0 && id <= 255 && pdp[id - 1] != NULL) {
// Cast Socket
SceNetAdhocPdpStat * socket = pdp[id - 1];
// Valid Data Buffer
if (data != NULL) {
// Valid Destination Address
if (daddr != NULL) {
// Log Destination
// Schedule Timeout Removal
if (flag) timeout = 0;
// Apply Send Timeout Settings to Socket
setsockopt(socket->id, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout));
// Single Target
if (!isBroadcastMAC(daddr)) {
// Fill in Target Structure
sockaddr_in target;
target.sin_family = AF_INET;
target.sin_port = htons(dport);
// Get Peer IP
if (resolveMAC((SceNetEtherAddr *)daddr, (uint32_t *)&target.sin_addr.s_addr)) {
// Acquire Network Lock
//_acquireNetworkLock();
// Send Data
changeBlockingMode(socket->id, flag);
int sent = sendto(socket->id, (const char *)data, len, 0, (sockaddr *)&target, sizeof(target));
int error = errno;
if (sent == SOCKET_ERROR) {
DEBUG_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend[%i:%u->%u] (size=%i)", error, id, getLocalPort(socket->id), ntohs(target.sin_port), len);
}
changeBlockingMode(socket->id, 0);
// Free Network Lock
//_freeNetworkLock();
uint8_t * sip = (uint8_t *)&target.sin_addr.s_addr;
// Sent Data
if (sent == len) {
DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Sent %u bytes to %u.%u.%u.%u:%u\n", id, getLocalPort(socket->id), sent, sip[0], sip[1], sip[2], sip[3], ntohs(target.sin_port));
// Success
return 0;
}
// Blocking Situation
if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK;
// Timeout
return ERROR_NET_ADHOC_TIMEOUT; //-1;
}
}
// Broadcast Target
else {
// Acquire Network Lock
//_acquireNetworkLock();
#ifdef BROADCAST_TO_LOCALHOST
//// Get Local IP Address
//union SceNetApctlInfo info; if (sceNetApctlGetInfo(PSP_NET_APCTL_INFO_IP, &info) == 0) {
// // Fill in Target Structure
// SceNetInetSockaddrIn target;
// target.sin_family = AF_INET;
// sceNetInetInetAton(info.ip, &target.sin_addr);
// target.sin_port = sceNetHtons(dport);
//
// // Send Data
// sceNetInetSendto(socket->id, data, len, ((flag != 0) ? (INET_MSG_DONTWAIT) : (0)), (SceNetInetSockaddr *)&target, sizeof(target));
//}
#endif
// Acquire Peer Lock
peerlock.lock();
// Iterate Peers
SceNetAdhocctlPeerInfo * peer = friends;
for (; peer != NULL; peer = peer->next) {
// Skip timed out friends
if (peer->last_recv == 0) continue;
// Fill in Target Structure
sockaddr_in target;
target.sin_family = AF_INET;
target.sin_addr.s_addr = peer->ip_addr;
target.sin_port = htons(dport);
// Send Data
changeBlockingMode(socket->id, flag);
int sent = sendto(socket->id, (const char *)data, len, 0, (sockaddr *)&target, sizeof(target));
int error = errno;
if (sent == SOCKET_ERROR) {
DEBUG_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend[%i:%u->%u](BC) [size=%i]", error, id, getLocalPort(socket->id), ntohs(target.sin_port), len);
}
changeBlockingMode(socket->id, 0);
if (sent >= 0) {
uint8_t * sip = (uint8_t *)&target.sin_addr.s_addr;
DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](BC): Sent %u bytes to %u.%u.%u.%u:%u\n", id, getLocalPort(socket->id), sent, sip[0], sip[1], sip[2], sip[3], ntohs(target.sin_port));
}
}
// Free Peer Lock
peerlock.unlock();
// Free Network Lock
//_freeNetworkLock();
// Success, Broadcast never fails!
return 0;
}
}
// Invalid Destination Address
return ERROR_NET_ADHOC_INVALID_ADDR;
}
// Invalid Argument
return ERROR_NET_ADHOC_INVALID_ARG;
}
// Invalid Socket ID
return ERROR_NET_ADHOC_INVALID_SOCKET_ID;
}
// Invalid Data Length
return ERROR_NET_ADHOC_INVALID_DATALEN;
}
// Invalid Destination Port
return ERROR_NET_ADHOC_INVALID_PORT;
}
// Library is uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
/**
* Adhoc Emulator PDP Receive Call
* @param id Socket File Descriptor
* @param saddr OUT: Source MAC Address
* @param sport OUT: Source Port
* @param buf OUT: Received Data
* @param len IN: Buffer Size OUT: Received Data Length
* @param timeout Receive Timeout
* @param flag Nonblocking Flag
* @return 0 on success or... ADHOC_INVALID_ARG, ADHOC_NOT_INITIALIZED, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_NOT_ENOUGH_SPACE, ADHOC_THREAD_ABORTED, NET_INTERNAL
*/
static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void *dataLength, u32 timeout, int flag) {
DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv(%i, %p, %p, %p, %p, %i, %i) at %08x", id, addr, port, buf, dataLength, timeout, flag, currentMIPS->pc);
if (!g_Config.bEnableWlan) {
return -1;
}
SceNetEtherAddr *saddr = (SceNetEtherAddr *)addr;
uint16_t * sport = (uint16_t *)port; //Looking at Quake3 sourcecode (net_adhoc.c) this is an "int" (32bit) but changing here to 32bit will cause FF-Type0 to see duplicated Host (thinking it was from a different host)
int * len = (int *)dataLength;
if (netAdhocInited) {
// Valid Socket ID
if (id > 0 && id <= 255 && pdp[id - 1] != NULL) {
// Cast Socket
SceNetAdhocPdpStat * socket = pdp[id - 1];
// Valid Arguments
if (saddr != NULL && port != NULL && buf != NULL && len != NULL && *len > 0) {
#ifndef PDP_DIRTY_MAGIC
// Schedule Timeout Removal
if (flag == 1) timeout = 0;
#else
// Nonblocking Simulator
int wouldblock = 0;
// Minimum Timeout
uint32_t mintimeout = 250000;
// Nonblocking Call
if (flag == 1) {
// Erase Nonblocking Flag
flag = 0;
// Set Wouldblock Behaviour
wouldblock = 1;
// Set Minimum Timeout (250ms)
if (timeout < mintimeout) timeout = mintimeout;
}
#endif
// Apply Receive Timeout Settings to Socket
setsockopt(socket->id, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
// Sender Address
sockaddr_in sin;
// Set Address Length (so we get the sender ip)
socklen_t sinlen = sizeof(sin);
//sin.sin_len = (uint8_t)sinlen;
// Acquire Network Lock
//_acquireNetworkLock();
// Receive Data
changeBlockingMode(socket->id, flag);
int received = recvfrom(socket->id, (char *)buf, *len,0,(sockaddr *)&sin, &sinlen);
int error = errno;
if (received == SOCKET_ERROR) {
VERBOSE_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpRecv [size=%i]", error, *len);
}
changeBlockingMode(socket->id, 0);
// Received Data
if (received >= 0) {
uint8_t * sip = (uint8_t *)&sin.sin_addr.s_addr;
DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %u.%u.%u.%u:%u\n", id, getLocalPort(socket->id), received, sip[0], sip[1], sip[2], sip[3], ntohs(sin.sin_port));
// Peer MAC
SceNetEtherAddr mac;
// Find Peer MAC
if (resolveIP(sin.sin_addr.s_addr, &mac)) {
// Provide Sender Information
*saddr = mac;
*sport = ntohs(sin.sin_port);
// Save Length
*len = received;
// Free Network Lock
//_freeNetworkLock();
// Return Success
return 0;
}
WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %u.%u.%u.%u:%u [%02X:%02X:%02X:%02X:%02X:%02X]", id, getLocalPort(socket->id), received, sip[0], sip[1], sip[2], sip[3], ntohs(sin.sin_port), mac.data[0], mac.data[1], mac.data[2], mac.data[3], mac.data[4], mac.data[5]);
// Free Network Lock
//_freeNetworkLock();
//Receiving data from unknown peer, ignore it ?
//return ERROR_NET_ADHOC_WOULD_BLOCK; //ERROR_NET_ADHOC_NO_DATA_AVAILABLE
}
// Free Network Lock
//_freeNetworkLock();
#ifdef PDP_DIRTY_MAGIC
// Restore Nonblocking Flag for Return Value
if (wouldblock) flag = 1;
#endif
// Nothing received
if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK;
return ERROR_NET_ADHOC_TIMEOUT;
}
// Invalid Argument
return ERROR_NET_ADHOC_INVALID_ARG;
}
// Invalid Socket ID
return ERROR_NET_ADHOC_INVALID_SOCKET_ID;
}
// Library is uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
// Flags seems to be bitmasks of ADHOC_F_ALERT...
int sceNetAdhocSetSocketAlert(int id, int flag) {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocSetSocketAlert(%d, %08x)", id, flag);
return 0; //Dummy Result
}
int sceNetAdhocPollSocket(u32 socketStructAddr, int count, int timeout, int nonblock) { // timeout in microseconds
DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocPollSocket(%08x, %i, %i, %i) at %08x", socketStructAddr, count, timeout, nonblock, currentMIPS->pc);
// Library is initialized
if (netAdhocInited)
{
SceNetAdhocPollSd * sds = NULL;
if (Memory::IsValidAddress(socketStructAddr)) sds = (SceNetAdhocPollSd *)Memory::GetPointer(socketStructAddr);
// Valid Arguments
if (sds != NULL && count > 0)
{
// Socket Check
int i = 0; for (; i < count; i++)
{
// Invalid Socket
if (sds[i].id < 1 || sds[i].id > 255 || (pdp[sds[i].id - 1] == NULL && ptp[sds[i].id - 1] == NULL)) return ERROR_NET_ADHOC_INVALID_SOCKET_ID;
}
// Nonblocking Mode
if (nonblock) timeout = 0;
// Prevent Nonblocking Mode
else
if (timeout == 0) timeout = 1;
int affectedsockets = 0;
if (count > FD_SETSIZE) count = FD_SETSIZE; // return affectedsockets;
// Acquire Network Lock
//acquireNetworkLock();
// Poll Sockets
//int affectedsockets = sceNetInetPoll(isds, count, timeout);
//WSAPoll only available for Vista or newer, so we'll use an alternative way for XP since Windows doesn't have poll function like *NIX
fd_set readfds, writefds, exceptfds;
int fd;
FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
// TODO: PDP and PTP should share the same indexing to prevent identical PDP & PTP socket id
for (int i = 0; i < count; i++) {
sds[i].revents = 0;
// Fill in Socket ID
if (ptp[sds[i].id - 1] != NULL) {
fd = ptp[sds[i].id - 1]->id;
if (ptp[sds[i].id - 1]->state == ADHOC_PTP_STATE_LISTEN) sds[i].revents |= ADHOC_EV_ACCEPT;
else
if (ptp[sds[i].id - 1]->state == ADHOC_PTP_STATE_CLOSED) sds[i].revents |= ADHOC_EV_CONNECT;
}
else {
fd = pdp[sds[i].id - 1]->id;
}
if (sds[i].events & ADHOC_EV_RECV) FD_SET(fd, &readfds);
if (sds[i].events & ADHOC_EV_SEND) FD_SET(fd, &writefds);
//if (sds[i].events & ADHOC_EV_ALERT)
FD_SET(fd, &exceptfds);
}
timeval tmout;
tmout.tv_sec = timeout / 1000000; // seconds
tmout.tv_usec = (timeout % 1000000); // microseconds
affectedsockets = select(count, &readfds, &writefds, &exceptfds, &tmout);
if (affectedsockets > 0) {
affectedsockets = 0;
for (int i = 0; i < count; i++) {
if (ptp[sds[i].id - 1] != NULL) {
fd = ptp[sds[i].id - 1]->id;
}
else {
fd = pdp[sds[i].id - 1]->id;
}
if (FD_ISSET(fd, &readfds)) sds[i].revents |= ADHOC_EV_RECV;
if (FD_ISSET(fd, &writefds)) sds[i].revents |= ADHOC_EV_SEND; // Data can always be sent ?
sds[i].revents &= sds[i].events;
if (FD_ISSET(fd, &exceptfds)) sds[i].revents |= ADHOC_EV_ALERT; // can be raised on revents regardless of events bitmask?
if (sds[i].revents) affectedsockets++;
}
}
// Free Network Lock
//freeNetworkLock();
// Blocking Mode (Nonblocking Mode returns 0, even on Success)
if (!nonblock)
{
// Success
if (affectedsockets >= 0) return affectedsockets; // (affectedsockets > 0)
// Timeout
return ERROR_NET_ADHOC_TIMEOUT;
}
// No Events generated
if (affectedsockets >= 0) return 0;
return ERROR_NET_ADHOC_WOULD_BLOCK; // Bleach 7 seems to use nonblocking and check the return value against 0 and 0x80410709 (also 0x80410717 ?)
}
// Invalid Argument
return ERROR_NET_ADHOC_INVALID_ARG;
}
// Library is uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
/**
* Adhoc Emulator PDP Socket Delete
* @param id Socket File Descriptor
* @param flag Bitflags (Unused)
* @return 0 on success or... ADHOC_INVALID_ARG, ADHOC_NOT_INITIALIZED, ADHOC_INVALID_SOCKET_ID
*/
static int sceNetAdhocPdpDelete(int id, int unknown) {
// WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right?
INFO_LOG(SCENET, "sceNetAdhocPdpDelete(%d, %d) at %08x", id, unknown, currentMIPS->pc);
/*
if (!g_Config.bEnableWlan) {
return 0;
}
*/
// Library is initialized
if (netAdhocInited) {
// Valid Arguments
if (id > 0 && id <= 255) {
// Cast Socket
SceNetAdhocPdpStat * sock = pdp[id - 1];
// Valid Socket
if (sock != NULL) {
// Close Connection
closesocket(sock->id);
// Remove Port Forward from Router
//sceNetPortClose("UDP", sock->lport);
// Free Memory
// free(sock);
// Free Translation Slot
pdp[id - 1] = NULL;
// Success
return 0;
}
// Invalid Socket ID
return ERROR_NET_ADHOC_INVALID_SOCKET_ID;
}
// Invalid Argument
return ERROR_NET_ADHOC_INVALID_ARG;
}
// Library is uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
static int sceNetAdhocctlGetAdhocId(u32 productStructAddr) {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlGetAdhocId(%08x)", productStructAddr);
// Library initialized
if (netAdhocctlInited)
{
// Valid Arguments
if (Memory::IsValidAddress(productStructAddr))
{
SceNetAdhocctlAdhocId * adhoc_id = (SceNetAdhocctlAdhocId *)Memory::GetPointer(productStructAddr);
// Copy Product ID
*adhoc_id = product_code;
//Memory::WriteStruct(productStructAddr, &product_code);
// Return Success
return 0;
}
// Invalid Arguments
return ERROR_NET_ADHOCCTL_INVALID_ARG;
}
// Library uninitialized
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
}
static int sceNetAdhocctlScan() {
INFO_LOG(SCENET, "sceNetAdhocctlScan() at %08x", currentMIPS->pc);
// Library initialized
if (netAdhocctlInited) {
// Not connected
if (threadStatus == ADHOCCTL_STATE_DISCONNECTED) {
threadStatus = ADHOCCTL_STATE_SCANNING;
// Prepare Scan Request Packet
uint8_t opcode = OPCODE_SCAN;
// Send Scan Request Packet, may failed with socket error 10054/10053 if someone else with the same IP already connected to AdHoc Server (the server might need to be modified to differentiate MAC instead of IP)
int iResult = send(metasocket, (char *)&opcode, 1, 0);
if (iResult == SOCKET_ERROR) {
int error = errno;
ERROR_LOG(SCENET, "Socket error (%i) when sending", error);
threadStatus = ADHOCCTL_STATE_DISCONNECTED;
//if (error == ECONNABORTED || error == ECONNRESET || error == ENOTCONN) return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // A case where it need to reconnect to AdhocServer
return ERROR_NET_ADHOCCTL_DISCONNECTED; // ERROR_NET_ADHOCCTL_BUSY
}
// Wait for Status to be connected to prevent Ford Street Racing from Failed to find game session
if (friendFinderRunning) {
int cnt = 0;
while ((threadStatus == ADHOCCTL_STATE_SCANNING) && (cnt < 5000)) {
sleep_ms(1);
cnt++;
}
}
// Return Success
return 0;
}
// Library is busy
return ERROR_NET_ADHOCCTL_BUSY; // ERROR_NET_ADHOCCTL_BUSY may trigger the game (ie. Ford Street Racing) to call sceNetAdhocctlDisconnect
}
// Library uninitialized
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
}
static int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) {
s32_le *buflen = NULL;
if (Memory::IsValidAddress(sizeAddr)) buflen = (s32_le *)Memory::GetPointer(sizeAddr);
SceNetAdhocctlScanInfoEmu *buf = NULL;
if (Memory::IsValidAddress(bufAddr)) buf = (SceNetAdhocctlScanInfoEmu *)Memory::GetPointer(bufAddr);
INFO_LOG(SCENET, "sceNetAdhocctlGetScanInfo([%08x]=%i, %08x)", sizeAddr, buflen ? *buflen : -1, bufAddr);
if (!g_Config.bEnableWlan) {
return 0;
}
// Library initialized
if (netAdhocctlInited) {
// Minimum Argument
if (buflen == NULL) return ERROR_NET_ADHOCCTL_INVALID_ARG;
// Minimum Argument Requirements
if (buflen != NULL) {
// Multithreading Lock
peerlock.lock();
// Length Returner Mode
if (buf == NULL) *buflen = countAvailableNetworks() * sizeof(SceNetAdhocctlScanInfoEmu);
// Normal Information Mode
else {
// Clear Memory
memset(buf, 0, *buflen);
// Network Discovery Counter
int discovered = 0;
// Count requested Networks
int requestcount = *buflen / sizeof(SceNetAdhocctlScanInfoEmu);
// Minimum Argument Requirements
if (requestcount > 0) {
// Group List Element
SceNetAdhocctlScanInfo * group = networks;
// Iterate Group List
for (; group != NULL && discovered < requestcount; group = group->next) {
// Copy Group Information
//buf[discovered] = *group;
buf[discovered].group_name = group->group_name;
buf[discovered].bssid = group->bssid;
buf[discovered].mode = group->mode;
// Exchange Adhoc Channel
// sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_ADHOC_CHANNEL, &buf[discovered].channel);
// Fake Channel Number 1 on Automatic Channel
// if (buf[discovered].channel == 0) buf[discovered].channel = 1;
//Always Fake Channel 1
buf[discovered].channel = 1;
// Increase Discovery Counter
discovered++;
}
// Link List
int i = 0; for (; i < discovered - 1; i++) {
// Link Network
buf[i].next = bufAddr + (sizeof(SceNetAdhocctlScanInfoEmu)*i) + sizeof(SceNetAdhocctlScanInfoEmu); // buf[i].next = &buf[i + 1];
}
// Fix Last Element
if (discovered > 0) buf[discovered - 1].next = 0;
}
// Fix Size
*buflen = discovered * sizeof(SceNetAdhocctlScanInfoEmu);
}
// Multithreading Unlock
peerlock.unlock();
// Return Success
return 0;
}
// Generic Error
return -1;
}
// Library uninitialized
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
}
// TODO: How many handlers can the PSP actually have for Adhocctl?
// TODO: Should we allow the same handler to be added more than once?
static u32 sceNetAdhocctlAddHandler(u32 handlerPtr, u32 handlerArg) {
bool foundHandler = false;
u32 retval = 0;
struct AdhocctlHandler handler;
memset(&handler, 0, sizeof(handler));
while (adhocctlHandlers.find(retval) != adhocctlHandlers.end())
++retval;
handler.entryPoint = handlerPtr;
handler.argument = handlerArg;
for (std::map<int, AdhocctlHandler>::iterator it = adhocctlHandlers.begin(); it != adhocctlHandlers.end(); it++) {
if (it->second.entryPoint == handlerPtr) {
foundHandler = true;
break;
}
}
if (!foundHandler && Memory::IsValidAddress(handlerPtr)) {
if (adhocctlHandlers.size() >= MAX_ADHOCCTL_HANDLERS) {
ERROR_LOG(SCENET, "UNTESTED sceNetAdhocctlAddHandler(%x, %x): Too many handlers", handlerPtr, handlerArg);
retval = ERROR_NET_ADHOCCTL_TOO_MANY_HANDLERS;
return retval;
}
adhocctlHandlers[retval] = handler;
WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlAddHandler(%x, %x): added handler %d", handlerPtr, handlerArg, retval);
} else if(foundHandler) {
ERROR_LOG(SCENET, "UNTESTED sceNetAdhocctlAddHandler(%x, %x): Same handler already exists", handlerPtr, handlerArg);
retval = 0; //Faking success
} else {
ERROR_LOG(SCENET, "UNTESTED sceNetAdhocctlAddHandler(%x, %x): Invalid handler", handlerPtr, handlerArg);
retval = ERROR_NET_ADHOCCTL_INVALID_ARG;
}
// The id to return is the number of handlers currently registered
return retval;
}
static u32 sceNetAdhocctlDisconnect() {
// WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right?
INFO_LOG(SCENET, "sceNetAdhocctlDisconnect() at %08x [group=%s]", currentMIPS->pc, parameter.group_name.data);
/*
if (!g_Config.bEnableWlan) {
return 0;
}
*/
// Library initialized
if (netAdhocctlInited) {
// Connected State (Adhoc Mode)
if (threadStatus != ADHOCCTL_STATE_DISCONNECTED) { // (threadStatus == ADHOCCTL_STATE_CONNECTED)
// Clear Network Name
memset(&parameter.group_name, 0, sizeof(parameter.group_name));
// Set Disconnected State
threadStatus = ADHOCCTL_STATE_DISCONNECTED;
// Set HUD Connection Status
//setConnectionStatus(0);
// Prepare Packet
uint8_t opcode = OPCODE_DISCONNECT;
// Acquire Network Lock
//_acquireNetworkLock();
// Send Disconnect Request Packet
int iResult = send(metasocket, (const char *)&opcode, 1, 0);
if (iResult == SOCKET_ERROR) {
ERROR_LOG(SCENET, "Socket error (%i) when sending", errno);
}
// Free Network Lock
//_freeNetworkLock();
// Multithreading Lock
//peerlock.lock();
// Clear Peer List
//freeFriendsRecursive(friends);
// Delete Peer Reference
//friends = NULL;
// Clear Group List
//freeGroupsRecursive(networks);
// Delete Group Reference
//networks = NULL;
// Multithreading Unlock
//peerlock.unlock();
}
// Notify Event Handlers (even if we weren't connected, not doing this will freeze games like God Eater, which expect this behaviour)
//__UpdateAdhocctlHandlers(ADHOCCTL_EVENT_DISCONNECT,0);
CoreTiming::ScheduleEvent_Threadsafe_Immediate(eventAdhocctlHandlerUpdate, join32(ADHOCCTL_EVENT_DISCONNECT, 0));
// Return Success, some games might ignore returned value and always treat it as success, otherwise repeatedly calling this function
return 0;
}
// Library uninitialized
return 0; //ERROR_NET_ADHOC_NOT_INITIALIZED; // Wipeout Pulse will repeatedly calling this function if returned value is ERROR_NET_ADHOC_NOT_INITIALIZED
}
static u32 sceNetAdhocctlDelHandler(u32 handlerID) {
if (adhocctlHandlers.find(handlerID) != adhocctlHandlers.end()) {
adhocctlHandlers.erase(handlerID);
WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlDelHandler(%d): deleted handler %d", handlerID, handlerID);
} else {
ERROR_LOG(SCENET, "UNTESTED sceNetAdhocctlDelHandler(%d): asked to delete invalid handler %d", handlerID, handlerID);
}
return 0;
}
int sceNetAdhocctlTerm() {
// WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right?
INFO_LOG(SCENET, "sceNetAdhocctlTerm()");
if (netAdhocMatchingInited) sceNetAdhocMatchingTerm();
if (netAdhocctlInited) {
netAdhocctlInited = false;
friendFinderRunning = false;
if (friendFinderThread.joinable()) {
friendFinderThread.join();
}
//May also need to clear Handlers
adhocctlHandlers.clear();
// Free stuff here
closesocket(metasocket);
metasocket = (int)INVALID_SOCKET;
/*#ifdef _MSC_VER
WSACleanup(); // Might be better to call WSAStartup/WSACleanup from sceNetInit/sceNetTerm isn't? since it's the first/last network function being used, even better to put it in __NetInit/__NetShutdown as it's only called once
#endif*/
}
return 0;
}
static int sceNetAdhocctlGetNameByAddr(const char *mac, u32 nameAddr) {
DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocctlGetNameByAddr(%s, %08x)", mac, nameAddr);
// Library initialized
if (netAdhocctlInited)
{
// Valid Arguments
if (mac != NULL && nameAddr != NULL)
{
SceNetAdhocctlNickname * nickname = NULL;
if (Memory::IsValidAddress(nameAddr)) nickname = (SceNetAdhocctlNickname *)Memory::GetPointer(nameAddr);
// Get Local MAC Address
SceNetEtherAddr localmac; getLocalMac(&localmac);
// Local MAC Matches
if (memcmp(&localmac, mac, sizeof(SceNetEtherAddr)) == 0)
{
// Write Data
*nickname = parameter.nickname;
// Return Success
return 0;
}
// Multithreading Lock
peerlock.lock();
// Peer Reference
SceNetAdhocctlPeerInfo * peer = friends;
// Iterate Peers
for (; peer != NULL; peer = peer->next)
{
// Match found
if (memcmp(&peer->mac_addr, mac, sizeof(SceNetEtherAddr)) == 0)
{
// Write Data
*nickname = peer->nickname;
// Multithreading Unlock
peerlock.unlock();
// Return Success
return 0;
}
}
// Multithreading Unlock
peerlock.unlock();
// Player not found
return ERROR_NET_ADHOC_NO_ENTRY;
}
// Invalid Arguments
return ERROR_NET_ADHOCCTL_INVALID_ARG;
}
// Library uninitialized
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
}
static int sceNetAdhocctlJoin(u32 scanInfoAddr) {
WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlJoin(%08x) at %08x", scanInfoAddr, currentMIPS->pc);
if (!g_Config.bEnableWlan) {
return -1;
}
// Library initialized
if (netAdhocctlInited)
{
// Valid Argument
if (Memory::IsValidAddress(scanInfoAddr))
{
SceNetAdhocctlScanInfoEmu * sinfo = (SceNetAdhocctlScanInfoEmu *)Memory::GetPointer(scanInfoAddr);
//while (true) sleep_ms(1);
// We can ignore minor connection process differences here
return sceNetAdhocctlCreate((const char*)&sinfo->group_name);
}
// Invalid Argument
return ERROR_NET_ADHOCCTL_INVALID_ARG;
}
// Uninitialized Library
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
}
int sceNetAdhocctlGetPeerInfo(const char *mac, int size, u32 peerInfoAddr) {
VERBOSE_LOG(SCENET, "sceNetAdhocctlGetPeerInfo(%s, %i, %08x) at %08x", mac, size, peerInfoAddr, currentMIPS->pc);
if (!g_Config.bEnableWlan) {
return -1;
}
SceNetEtherAddr * maddr = (SceNetEtherAddr *)mac;
SceNetAdhocctlPeerInfoEmu * buf = NULL;
if (Memory::IsValidAddress(peerInfoAddr)) {
buf = (SceNetAdhocctlPeerInfoEmu *)Memory::GetPointer(peerInfoAddr);
}
// Library initialized
if (netAdhocctlInited) {
if ((size < sizeof(SceNetAdhocctlPeerInfoEmu)) || (buf == NULL)) return ERROR_NET_ADHOCCTL_INVALID_ARG;
int retval = ERROR_NET_ADHOCCTL_INVALID_ARG; // -1;
// Local MAC
if (isLocalMAC(maddr)) {
sockaddr_in addr;
SceNetAdhocctlNickname nickname;
getLocalIp(&addr);
strcpy((char*)nickname.data, g_Config.sNickName.c_str());
//buf->next = 0;
buf->nickname = nickname;
buf->nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char
buf->mac_addr = *maddr;
buf->ip_addr = addr.sin_addr.s_addr; // 0x11111111;
//buf->padding = 0x1111; //0;
buf->last_recv = CoreTiming::GetGlobalTimeUsScaled();
// Success
retval = 0;
}
// Find Peer by MAC
else
{
// Multithreading Lock
peerlock.lock();
SceNetAdhocctlPeerInfo * peer = findFriend(maddr);
if (peer != NULL) {
// Fake Receive Time
if (peer->last_recv != 0) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
//buf->next = 0;
buf->nickname = peer->nickname;
buf->nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char
buf->mac_addr = *maddr;
buf->ip_addr = peer->ip_addr; // 0x11111111;
//buf->padding = /*0;*/ 0x1111;
buf->last_recv = peer->last_recv; //CoreTiming::GetGlobalTimeUsScaled(); //real_time_now()*1000000.0; //(uint64_t)time(NULL); //This timestamp is important issue on Dissidia 012
// Success
retval = 0;
}
// Multithreading Unlock
peerlock.unlock();
}
return retval;
}
// Library uninitialized
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
}
/**
* Create and / or Join a Virtual Network of the specified Name
* @param group_name Virtual Network Name
* @return 0 on success or... ADHOCCTL_NOT_INITIALIZED, ADHOCCTL_INVALID_ARG, ADHOCCTL_BUSY
*/
int sceNetAdhocctlCreate(const char *groupName) {
INFO_LOG(SCENET, "sceNetAdhocctlCreate(%s) at %08x", groupName, currentMIPS->pc);
if (!g_Config.bEnableWlan) {
return -1;
}
const SceNetAdhocctlGroupName * groupNameStruct = (const SceNetAdhocctlGroupName *)groupName;
// Library initialized
if (netAdhocctlInited) {
// Valid Argument
if (validNetworkName(groupNameStruct)) {
// Disconnected State, may also need to check for Scanning state to prevent some games from failing to host a game session
if ((threadStatus == ADHOCCTL_STATE_DISCONNECTED) || (threadStatus == ADHOCCTL_STATE_SCANNING)) {
// Set Network Name
if (groupNameStruct != NULL) parameter.group_name = *groupNameStruct;
// Reset Network Name
else memset(&parameter.group_name, 0, sizeof(parameter.group_name));
// Prepare Connect Packet
SceNetAdhocctlConnectPacketC2S packet;
// Clear Packet Memory
memset(&packet, 0, sizeof(packet));
// Set Packet Opcode
packet.base.opcode = OPCODE_CONNECT;
// Set Target Group
if (groupNameStruct != NULL) packet.group = *groupNameStruct;
// Acquire Network Lock
// Send Packet
int iResult = send(metasocket, (const char *)&packet, sizeof(packet), 0);
if (iResult == SOCKET_ERROR) {
ERROR_LOG(SCENET, "Socket error (%i) when sending", errno);
//return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // ERROR_NET_ADHOCCTL_DISCONNECTED; // ERROR_NET_ADHOCCTL_BUSY;
//Faking success, to prevent Full Auto 2 from freezing while Initializing Network
threadStatus = ADHOCCTL_STATE_CONNECTED;
// Notify Event Handlers, Needed for the Nickname to be shown on the screen when success is faked
// Might be better not to notify the game when faking success (failed to connect to adhoc server), at least the player will know that it failed to connect
//__UpdateAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); //CoreTiming::ScheduleEvent_Threadsafe_Immediate(eventAdhocctlHandlerUpdate, join32(ADHOCCTL_EVENT_CONNECT, 0));
}
// Free Network Lock
// Set HUD Connection Status
//setConnectionStatus(1);
// Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session
if (friendFinderRunning) {
int cnt = 0;
while ((threadStatus != ADHOCCTL_STATE_CONNECTED) && (cnt < 5000)) {
sleep_ms(1);
cnt++;
}
}
// Return Success
return 0;
}
// Connected State
return ERROR_NET_ADHOCCTL_BUSY; // ERROR_NET_ADHOCCTL_BUSY may trigger the game (ie. Ford Street Racing) to call sceNetAdhocctlDisconnect
}
// Invalid Argument
return ERROR_NET_ADHOC_INVALID_ARG;
}
// Library uninitialized
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
}
static int sceNetAdhocctlConnect(u32 ptrToGroupName) {
if (Memory::IsValidAddress(ptrToGroupName)) {
INFO_LOG(SCENET, "sceNetAdhocctlConnect(groupName=%s) at %08x", Memory::GetCharPointer(ptrToGroupName), currentMIPS->pc);
return sceNetAdhocctlCreate(Memory::GetCharPointer(ptrToGroupName));
}
return ERROR_NET_ADHOC_INVALID_ARG; // ERROR_NET_ADHOC_INVALID_ADDR;
}
static int sceNetAdhocctlCreateEnterGameMode(const char *groupName, int unknown, int playerNum, u32 macsAddr, int timeout, int unknown2) {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlCreateEnterGameMode(%s, %i, %i, %08x, %i, %i) at %08x", groupName, unknown, playerNum, macsAddr, timeout, unknown2, currentMIPS->pc);
return -1;
}
static int sceNetAdhocctlJoinEnterGameMode(const char *groupName, const char *macAddr, int timeout, int unknown2) {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlJoinEnterGameMode(%s, %s, %i, %i) at %08x", groupName, macAddr, timeout, unknown2, currentMIPS->pc);
return -1;
}
/**
* Create and Join a GameMode Network as Host (with Minimum Peer Check)
* @param group_name Virtual Network Name
* @param game_type Network Type (1A, 1B, 2A)
* @param min_members Minimum Number of Peers
* @param num_members Total Number of Peers (including Host)
* @param members MAC Address List of Peers (own MAC at Index 0)
* @param timeout Timeout Value (in Microseconds)
* @param flag Unused Bitflags
* @return 0 on success or... ADHOCCTL_NOT_INITIALIZED, ADHOCCTL_INVALID_ARG, ADHOCCTL_BUSY, ADHOCCTL_CHANNEL_NOT_AVAILABLE
*/
int sceNetAdhocctlCreateEnterGameModeMin(const char *group_name, int game_type, int min_members, int num_members, u32 membersAddr, u32 timeout, int flag)
{
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlCreateEnterGameModeMin(%s, %i, %i, %i, %08x, %d, %i) at %08x", group_name, game_type, min_members, num_members, membersAddr, timeout, flag, currentMIPS->pc);
// We don't really need the Minimum User Check
return sceNetAdhocctlCreateEnterGameMode(group_name, game_type, num_members, membersAddr, timeout, flag);
}
int sceNetAdhocTerm() {
INFO_LOG(SCENET, "sceNetAdhocTerm()");
// WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup all the sockets right?
if (netAdhocctlInited) sceNetAdhocctlTerm();
// Library is initialized
if (netAdhocInited) {
// Delete fake PSP Thread
if (threadAdhocID != 0) {
__KernelStopThread(threadAdhocID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocThread stopped");
__KernelDeleteThread(threadAdhocID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocThread deleted");
}
// Delete PDP Sockets
deleteAllPDP();
// Delete PTP Sockets
deleteAllPTP();
// Delete Gamemode Buffer
//deleteAllGMB();
// Terminate Internet Library
//sceNetInetTerm();
//Kill AdhocServer Thread
if (adhocServerRunning) {
adhocServerRunning = false;
if (adhocServerThread.joinable()) {
adhocServerThread.join();
}
}
// Unload Internet Modules (Just keep it in memory... unloading crashes?!)
// if (_manage_modules != 0) sceUtilityUnloadModule(PSP_MODULE_NET_INET);
// Library shutdown
netAdhocInited = false;
return 0;
} else {
// Seems to return this when called a second time after being terminated without another initialisation
return SCE_KERNEL_ERROR_LWMUTEX_NOT_FOUND;
}
}
static int sceNetAdhocGetPdpStat(u32 structSize, u32 structAddr) {
VERBOSE_LOG(SCENET, "UNTESTED sceNetAdhocGetPdpStat(%08x, %08x) at %08x", structSize, structAddr, currentMIPS->pc);
// Library is initialized
if (netAdhocInited)
{
s32_le *buflen = NULL;
if (Memory::IsValidAddress(structSize)) buflen = (s32_le *)Memory::GetPointer(structSize);
SceNetAdhocPdpStat *buf = NULL;
if (Memory::IsValidAddress(structAddr)) buf = (SceNetAdhocPdpStat *)Memory::GetPointer(structAddr);
// Length Returner Mode
if (buflen != NULL && buf == NULL)
{
// Return Required Size
*buflen = sizeof(SceNetAdhocPdpStat) * getPDPSocketCount();
// Success
return 0;
}
// Status Returner Mode
else if (buflen != NULL && buf != NULL)
{
// Socket Count
int socketcount = getPDPSocketCount();
// Figure out how many Sockets we will return
int count = *buflen / sizeof(SceNetAdhocPdpStat);
if (count > socketcount) count = socketcount;
// Copy Counter
int i = 0;
// Iterate Translation Table
int j = 0; for (; j < 255 && i < count; j++)
{
// Valid Socket Entry
if (pdp[j] != NULL)
{
// Copy Socket Data from Internal Memory
buf[i] = *pdp[j];
// Fix Client View Socket ID
buf[i].id = j + 1;
// Write End of List Reference
buf[i].next = 0;
// Link Previous Element
if (i > 0)
buf[i - 1].next = structAddr + ((i - 1) * sizeof(SceNetAdhocPdpStat)) + sizeof(SceNetAdhocPdpStat); //buf[i - 1].next = &buf[i];
// Increment Counter
i++;
}
}
// Update Buffer Length
*buflen = i * sizeof(SceNetAdhocPdpStat);
// Success
return 0;
}
// Invalid Arguments
return ERROR_NET_ADHOC_INVALID_ARG;
}
// Library is uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
/**
* Adhoc Emulator PTP Socket List Getter
* @param buflen IN: Length of Buffer in Bytes OUT: Required Length of Buffer in Bytes
* @param buf PTP Socket List Buffer (can be NULL if you wish to receive Required Length)
* @return 0 on success or... ADHOC_INVALID_ARG, ADHOC_NOT_INITIALIZED
*/
static int sceNetAdhocGetPtpStat(u32 structSize, u32 structAddr) {
// Spams a lot
VERBOSE_LOG(SCENET,"sceNetAdhocGetPtpStat(%08x, %08x) at %08x",structSize,structAddr,currentMIPS->pc);
s32_le *buflen = NULL;
if (Memory::IsValidAddress(structSize)) buflen = (s32_le *)Memory::GetPointer(structSize);
SceNetAdhocPtpStat *buf = NULL;
if (Memory::IsValidAddress(structAddr)) buf = (SceNetAdhocPtpStat *)Memory::GetPointer(structAddr);
// Library is initialized
if (netAdhocInited) {
// Length Returner Mode
if (buflen != NULL && buf == NULL) {
// Return Required Size
*buflen = sizeof(SceNetAdhocPtpStat) * getPTPSocketCount();
// Success
return 0;
}
// Status Returner Mode
else if (buflen != NULL && buf != NULL) {
// Socket Count
int socketcount = getPTPSocketCount();
// Figure out how many Sockets we will return
int count = *buflen / sizeof(SceNetAdhocPtpStat);
if (count > socketcount) count = socketcount;
// Copy Counter
int i = 0;
// Iterate Sockets
int j = 0; for (; j < 255 && i < count; j++) {
// Active Socket
if (ptp[j] != NULL) {
// Copy Socket Data from internal Memory
buf[i] = *ptp[j];
// Fix Client View Socket ID
buf[i].id = j + 1;
// Write End of List Reference
buf[i].next = 0;
// Link previous Element to this one
if (i > 0)
buf[i - 1].next = structAddr + ((i - 1) * sizeof(SceNetAdhocPtpStat)) + sizeof(SceNetAdhocPtpStat);
// Increment Counter
i++;
}
}
// Update Buffer Length
*buflen = i * sizeof(SceNetAdhocPtpStat);
// Success
return 0;
}
// Invalid Arguments
return ERROR_NET_ADHOC_INVALID_ARG;
}
// Library is uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
/**
* Adhoc Emulator PTP Active Socket Creator
* @param saddr Local MAC (Unused)
* @param sport Local Binding Port
* @param daddr Target MAC
* @param dport Target Port
* @param bufsize Socket Buffer Size
* @param rexmt_int Retransmit Interval (in Microseconds)
* @param rexmt_cnt Retransmit Count
* @param flag Bitflags (Unused)
* @return Socket ID > 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_ADDR, ADHOC_INVALID_PORT
*/
static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, int dport, int bufsize, int rexmt_int, int rexmt_cnt, int unknown) {
INFO_LOG(SCENET, "sceNetAdhocPtpOpen(%s,%d,%s,%d,%d,%d,%d,%d)", srcmac, sport, dstmac,dport,bufsize, rexmt_int, rexmt_cnt, unknown);
if (!g_Config.bEnableWlan) {
return 0;
}
SceNetEtherAddr * saddr = (SceNetEtherAddr *)srcmac;
SceNetEtherAddr * daddr = (SceNetEtherAddr *)dstmac;
// Library is initialized
if (netAdhocInited) {
// Valid Addresses
if (saddr != NULL && isLocalMAC(saddr) && daddr != NULL && !isBroadcastMAC(daddr)) {
// Random Port required
if (sport == 0) {
// Find unused Port
// while (sport == 0 || _IsPTPPortInUse(sport)) {
// // Generate Port Number
// sport = (uint16_t)_getRandomNumber(65535);
// }
}
// Valid Ports
if (!isPTPPortInUse(sport) && dport != 0) {
// Valid Arguments
if (bufsize > 0 && rexmt_int > 0 && rexmt_cnt > 0) {
// Create Infrastructure Socket
int tcpsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Valid Socket produced
if (tcpsocket > 0) {
// Change socket buffer size when necessary
if (getSockBufferSize(tcpsocket, SO_SNDBUF) < bufsize) setSockBufferSize(tcpsocket, SO_SNDBUF, bufsize);
if (getSockBufferSize(tcpsocket, SO_RCVBUF) < bufsize) setSockBufferSize(tcpsocket, SO_RCVBUF, bufsize);
// Enable Port Re-use
setsockopt(tcpsocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&one, sizeof(one));
// Binding Information for local Port
sockaddr_in addr;
// addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(sport);
// Bound Socket to local Port
if (bind(tcpsocket, (sockaddr *)&addr, sizeof(addr)) == 0) {
// Update sport with the port assigned by bind
socklen_t len = sizeof(addr);
if (getsockname(tcpsocket, (sockaddr *)&addr, &len) == 0) {
sport = ntohs(addr.sin_port);
}
// Allocate Memory
SceNetAdhocPtpStat * internal = (SceNetAdhocPtpStat *)malloc(sizeof(SceNetAdhocPtpStat));
// Allocated Memory
if (internal != NULL) {
// Find Free Translator ID
int i = 0; for (; i < 255; i++) if (ptp[i] == NULL) break;
// Found Free Translator ID
if (i < 255) {
// Clear Memory
memset(internal, 0, sizeof(SceNetAdhocPtpStat));
// Copy Infrastructure Socket ID
internal->id = tcpsocket;
// Copy Address Information
internal->laddr = *saddr;
internal->paddr = *daddr;
internal->lport = sport;
internal->pport = dport;
// Set Buffer Size
internal->rcv_sb_cc = bufsize;
// Link PTP Socket
ptp[i] = internal;
// Add Port Forward to Router
// sceNetPortOpen("TCP", sport);
// Return PTP Socket Pointer
return i + 1;
}
// Free Memory
free(internal);
}
}
// Close Socket
closesocket(tcpsocket);
}
}
// Invalid Arguments
return ERROR_NET_ADHOC_INVALID_ARG;
}
// Invalid Ports
return ERROR_NET_ADHOC_INVALID_PORT;
}
// Invalid Addresses
return ERROR_NET_ADHOC_INVALID_ADDR;
}
return 0;
}
/**
* Adhoc Emulator PTP Connection Acceptor
* @param id Socket File Descriptor
* @param addr OUT: Peer MAC Address
* @param port OUT: Peer Port
* @param timeout Accept Timeout (in Microseconds)
* @param flag Nonblocking Flag
* @return Socket ID >= 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_SOCKET_ALERTED, ADHOC_SOCKET_ID_NOT_AVAIL, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_NOT_LISTENED, ADHOC_THREAD_ABORTED, NET_INTERNAL
*/
static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int timeout, int flag) {
SceNetEtherAddr * addr = NULL;
if (Memory::IsValidAddress(peerMacAddrPtr)) {
addr = PSPPointer<SceNetEtherAddr>::Create(peerMacAddrPtr);
}
uint16_t * port = NULL; //
if (Memory::IsValidAddress(peerPortPtr)) {
port = (uint16_t *)Memory::GetPointer(peerPortPtr);
}
DEBUG_LOG(SCENET, "sceNetAdhocPtpAccept(%d,%08x,[%08x]=%u,%d,%u) at %08x", id, peerMacAddrPtr, peerPortPtr, port ? *port : -1, timeout, flag, currentMIPS->pc);
if (!g_Config.bEnableWlan) {
return 0;
}
// Library is initialized
if (netAdhocInited) {
// Valid Socket
if (id > 0 && id <= 255 && ptp[id - 1] != NULL) {
// Cast Socket
SceNetAdhocPtpStat * socket = ptp[id - 1];
// Listener Socket
if (socket->state == ADHOC_PTP_STATE_LISTEN) {
// Valid Arguments
if (addr != NULL /*&& port != NULL*/) { //GTA:VCS seems to use 0 for the portPtr
// Address Information
sockaddr_in peeraddr;
memset(&peeraddr, 0, sizeof(peeraddr));
socklen_t peeraddrlen = sizeof(peeraddr);
// Local Address Information
sockaddr_in local;
memset(&local, 0, sizeof(local));
socklen_t locallen = sizeof(local);
// Grab Nonblocking Flag
uint32_t nbio = getBlockingFlag(socket->id);
// Switch to Nonblocking Behaviour
if (nbio == 0) {
// Overwrite Socket Option
changeBlockingMode(socket->id,1);
}
// Accept Connection
int newsocket = accept(socket->id, (sockaddr *)&peeraddr, &peeraddrlen);
// Blocking Behaviour
if (!flag && newsocket == -1) {
// Get Start Time
uint32_t starttime = (uint32_t)(real_time_now()*1000000.0);
// Retry until Timeout hits
while ((timeout == 0 ||((uint32_t)(real_time_now()*1000000.0) - starttime) < (uint32_t)timeout) && newsocket == -1) {
// Accept Connection
newsocket = accept(socket->id, (sockaddr *)&peeraddr, &peeraddrlen);
// Wait a bit...
sleep_ms(1);
}
}
if (newsocket == SOCKET_ERROR) {
int error = errno;
DEBUG_LOG(SCENET, "sceNetAdhocPtpAccept[%i]: Socket Error (%i)", id, error);
}
// Restore Blocking Behaviour
if (nbio == 0) {
// Restore Socket Option
changeBlockingMode(socket->id,0);
}
// Accepted New Connection
if (newsocket > 0) {
// Do we need to Change socket buffer size to match the listener buffer size?
// Enable Port Re-use
setsockopt(newsocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&one, sizeof(one));
// Grab Local Address
if (getsockname(newsocket, (sockaddr *)&local, &locallen) == 0) {
// Peer MAC
SceNetEtherAddr mac;
// Find Peer MAC
if (resolveIP(peeraddr.sin_addr.s_addr, &mac)) {
// Allocate Memory
SceNetAdhocPtpStat * internal = (SceNetAdhocPtpStat *)malloc(sizeof(SceNetAdhocPtpStat));
// Allocated Memory
if (internal != NULL) {
// Find Free Translator ID
int i = 0; for (; i < 255; i++) if (ptp[i] == NULL) break;
// Found Free Translator ID
if (i < 255) {
// Clear Memory
memset(internal, 0, sizeof(SceNetAdhocPtpStat));
// Copy Socket Descriptor to Structure
internal->id = newsocket;
// Set Buffer Size
if (getSockBufferSize(newsocket, SO_RCVBUF) < socket->rcv_sb_cc) setSockBufferSize(newsocket, SO_RCVBUF, socket->rcv_sb_cc);
if (getSockBufferSize(newsocket, SO_SNDBUF) < socket->snd_sb_cc) setSockBufferSize(newsocket, SO_SNDBUF, socket->snd_sb_cc);
internal->rcv_sb_cc = socket->rcv_sb_cc;
internal->snd_sb_cc = socket->snd_sb_cc;
// Copy Local Address Data to Structure
getLocalMac(&internal->laddr);
internal->lport = ntohs(local.sin_port);
// Copy Peer Address Data to Structure
internal->paddr = mac;
internal->pport = ntohs(peeraddr.sin_port);
// Set Connected State
internal->state = ADHOC_PTP_STATE_ESTABLISHED;
// Return Peer Address Information
*addr = internal->paddr;
if (port != NULL) *port = internal->pport;
// Link PTP Socket
ptp[i] = internal;
// Add Port Forward to Router
// sceNetPortOpen("TCP", internal->lport);
uint8_t * pip = (uint8_t *)&peeraddr.sin_addr.s_addr;
INFO_LOG(SCENET, "sceNetAdhocPtpAccept[%i->%i:%u]: Established (%u.%u.%u.%u:%u)", id, i+1, internal->lport, pip[0], pip[1], pip[2], pip[3], internal->pport);
// Return Socket
return i + 1;
}
// Free Memory
free(internal);
}
}
}
// Close Socket
closesocket(newsocket);
ERROR_LOG(SCENET, "sceNetAdhocPtpAccept[%i]: Failed (Socket Closed)", id);
}
// Action would block
if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK;
// Timeout
return ERROR_NET_ADHOC_TIMEOUT;
}
// Invalid Arguments
return ERROR_NET_ADHOC_INVALID_ARG;
}
// Client Socket
return ERROR_NET_ADHOC_NOT_LISTENED;
}
// Invalid Socket
return ERROR_NET_ADHOC_INVALID_SOCKET_ID;
}
// Library is uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
/**
* Adhoc Emulator PTP Connection Opener
* @param id Socket File Descriptor
* @param timeout Connect Timeout (in Microseconds)
* @param flag Nonblocking Flag
* @return 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_CONNECTION_REFUSED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_NOT_OPENED, ADHOC_THREAD_ABORTED, NET_INTERNAL
*/
static int sceNetAdhocPtpConnect(int id, int timeout, int flag) {
INFO_LOG(SCENET, "sceNetAdhocPtpConnect(%i, %i, %08x) at %08x", id, timeout, flag, currentMIPS->pc);
if (!g_Config.bEnableWlan) {
return 0;
}
// Library is initialized
if (netAdhocInited)
{
// Valid Socket
if (id > 0 && id <= 255 && ptp[id - 1] != NULL) {
// Cast Socket
SceNetAdhocPtpStat * socket = ptp[id - 1];
// Valid Client Socket
if (socket->state == 0) {
// Target Address
sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
// Setup Target Address
// sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_port = htons(socket->pport);
// Grab Peer IP
if (resolveMAC(&socket->paddr, (uint32_t *)&sin.sin_addr.s_addr)) {
// Grab Nonblocking Flag
uint32_t nbio = getBlockingFlag(socket->id);
// Switch to Nonblocking Behaviour
if (nbio == 0) {
// Overwrite Socket Option
changeBlockingMode(socket->id, 1);
}
// Connect Socket to Peer (Nonblocking)
int connectresult = connect(socket->id, (sockaddr *)&sin, sizeof(sin));
// Grab Error Code
int errorcode = errno;
if (connectresult == SOCKET_ERROR) {
ERROR_LOG(SCENET, "sceNetAdhocPtpConnect[%i]: Socket Error (%i)", id, errorcode);
}
// Restore Blocking Behaviour
if (nbio == 0) {
// Restore Socket Option
changeBlockingMode(socket->id,0);
}
// Instant Connection (Lucky!)
if (connectresult == 0 || (connectresult == -1 && (errorcode == EISCONN /*|| errorcode == EALREADY)*/))) {
// Set Connected State
socket->state = ADHOC_PTP_STATE_ESTABLISHED;
INFO_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Already Connected", id, socket->lport);
// Success
return 0;
}
// Connection in Progress
else if (connectresult == -1 && connectInProgress(errorcode)) {
// Nonblocking Mode
if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK;
// Blocking Mode
else {
// Grab Connection Start Time
uint32_t starttime = (uint32_t)(real_time_now()*1000000.0);
// Peer Information (for Connection-Polling)
sockaddr_in peer;
memset(&peer, 0, sizeof(peer));
socklen_t peerlen = sizeof(peer);
// Wait for Connection
while ((timeout == 0 || ( (uint32_t)(real_time_now()*1000000.0) - starttime) < (uint32_t)timeout) && getpeername(socket->id, (sockaddr *)&peer, &peerlen) != 0) {
// Wait 1ms
sleep_ms(1);
}
// Connected in Time
if (sin.sin_addr.s_addr == peer.sin_addr.s_addr/* && sin.sin_port == peer.sin_port*/) {
// Set Connected State
socket->state = ADHOC_PTP_STATE_ESTABLISHED;
uint8_t * pip = (uint8_t *)&peer.sin_addr.s_addr;
INFO_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Established (%u.%u.%u.%u:%u)", id, socket->lport, pip[0], pip[1], pip[2], pip[3], socket->pport);
// Success
return 0;
}
// Timeout occured
return ERROR_NET_ADHOC_TIMEOUT;
}
}
}
// Peer not found
return ERROR_NET_ADHOC_CONNECTION_REFUSED;
}
// Not a valid Client Socket
return ERROR_NET_ADHOC_NOT_OPENED;
}
// Invalid Socket
return ERROR_NET_ADHOC_INVALID_SOCKET_ID;
}
// Library is uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
/**
* Adhoc Emulator PTP Socket Closer
* @param id Socket File Descriptor
* @param flag Bitflags (Unused)
* @return 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED
*/
static int sceNetAdhocPtpClose(int id, int unknown) {
INFO_LOG(SCENET,"sceNetAdhocPtpClose(%d,%d) at %08x",id,unknown,currentMIPS->pc);
if (!g_Config.bEnableWlan) {
return 0;
}
// Library is initialized
if (netAdhocInited) {
// Valid Arguments & Atleast one Socket
if (id > 0 && id <= 255 && ptp[id - 1] != NULL) {
// Cast Socket
SceNetAdhocPtpStat * socket = ptp[id - 1];
// Close Connection
closesocket(socket->id);
// Remove Port Forward from Router
// sceNetPortClose("TCP", socket->lport);
// Free Memory
free(socket);
// Free Reference
ptp[id - 1] = NULL;
// Success
return 0;
}
// Invalid Argument
return ERROR_NET_ADHOC_INVALID_SOCKET_ID;
}
// Library is uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
/**
* Adhoc Emulator PTP Passive Socket Creator
* @param saddr Local MAC (Unused)
* @param sport Local Binding Port
* @param bufsize Socket Buffer Size
* @param rexmt_int Retransmit Interval (in Microseconds)
* @param rexmt_cnt Retransmit Count
* @param backlog Size of Connection Queue
* @param flag Bitflags (Unused)
* @return Socket ID > 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_ADDR, ADHOC_INVALID_PORT, ADHOC_SOCKET_ID_NOT_AVAIL, ADHOC_PORT_NOT_AVAIL, ADHOC_PORT_IN_USE, NET_NO_SPACE
*/
static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int rexmt_int, int rexmt_cnt, int backlog, int unk) {
INFO_LOG(SCENET, "sceNetAdhocPtpListen(%s,%d,%d,%d,%d,%d,%d)",srcmac,sport,bufsize,rexmt_int,rexmt_cnt,backlog,unk);
if (!g_Config.bEnableWlan) {
return 0;
}
// Library is initialized
SceNetEtherAddr * saddr = (SceNetEtherAddr *)srcmac;
if (netAdhocInited) {
// Valid Address
if (saddr != NULL && isLocalMAC(saddr))
{
// Random Port required
if (sport == 0) {
// Find unused Port
// while (sport == 0 || __IsPTPPortInUse(sport))
// {
// // Generate Port Number
// sport = (uint16_t)_getRandomNumber(65535);
// }
}
// Valid Ports
if (!isPTPPortInUse(sport)) {
// Valid Arguments
if (bufsize > 0 && rexmt_int > 0 && rexmt_cnt > 0 && backlog > 0)
{
// Create Infrastructure Socket
int tcpsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Valid Socket produced
if (tcpsocket > 0) {
// Change socket buffer size when necessary
if (getSockBufferSize(tcpsocket, SO_SNDBUF) < bufsize) setSockBufferSize(tcpsocket, SO_SNDBUF, bufsize);
if (getSockBufferSize(tcpsocket, SO_RCVBUF) < bufsize) setSockBufferSize(tcpsocket, SO_RCVBUF, bufsize);
// Enable Port Re-use
setsockopt(tcpsocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&one, sizeof(one));
// Binding Information for local Port
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(sport);
int iResult = 0;
// Bound Socket to local Port
if ((iResult = bind(tcpsocket, (sockaddr *)&addr, sizeof(addr))) == 0) {
// Switch into Listening Mode
if ((iResult = listen(tcpsocket, backlog)) == 0) {
// Allocate Memory
SceNetAdhocPtpStat * internal = (SceNetAdhocPtpStat *)malloc(sizeof(SceNetAdhocPtpStat));
// Allocated Memory
if (internal != NULL) {
// Find Free Translator ID
int i = 0; for (; i < 255; i++) if (ptp[i] == NULL) break;
// Found Free Translator ID
if (i < 255) {
// Clear Memory
memset(internal, 0, sizeof(SceNetAdhocPtpStat));
// Copy Infrastructure Socket ID
internal->id = tcpsocket;
// Copy Address Information
internal->laddr = *saddr;
internal->lport = sport;
// Flag Socket as Listener
internal->state = ADHOC_PTP_STATE_LISTEN;
// Set Buffer Size
internal->rcv_sb_cc = bufsize;
// Link PTP Socket
ptp[i] = internal;
// Add Port Forward to Router
// sceNetPortOpen("TCP", sport);
// Return PTP Socket Pointer
return i + 1;
}
// Free Memory
free(internal);
}
}
}
if (iResult == SOCKET_ERROR) {
int error = errno;
ERROR_LOG(SCENET, "sceNetAdhocPtpListen[%i]: Socket Error (%i)", sport, error);
}
// Close Socket
closesocket(tcpsocket);
// Port not available (exclusively in use?)
return ERROR_NET_ADHOC_PORT_NOT_AVAIL;
}
// Socket not available
return ERROR_NET_ADHOC_SOCKET_ID_NOT_AVAIL;
}
// Invalid Arguments
return ERROR_NET_ADHOC_INVALID_ARG;
}
// Invalid Ports
return ERROR_NET_ADHOC_PORT_IN_USE;
}
// Invalid Addresses
return ERROR_NET_ADHOC_INVALID_ADDR;
}
// Library is uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
/**
* Adhoc Emulator PTP Sender
* @param id Socket File Descriptor
* @param data Data Payload
* @param len IN: Length of Payload OUT: Sent Data (in Bytes)
* @param timeout Send Timeout (in Microseconds)
* @param flag Nonblocking Flag
* @return 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_NOT_CONNECTED, ADHOC_THREAD_ABORTED, ADHOC_INVALID_DATALEN, ADHOC_DISCONNECTED, NET_INTERNAL, NET_NO_SPACE
*/
static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeout, int flag) {
DEBUG_LOG(SCENET, "sceNetAdhocPtpSend(%d,%08x,%08x,%d,%d) at %08x", id, dataAddr, dataSizeAddr, timeout, flag, currentMIPS->pc);
if (!g_Config.bEnableWlan) {
return 0;
}
int * len = (int *)Memory::GetPointer(dataSizeAddr);
const char * data = Memory::GetCharPointer(dataAddr);
// Library is initialized
if (netAdhocInited) {
// Valid Socket
if (id > 0 && id <= 255 && ptp[id - 1] != NULL) {
// Cast Socket
SceNetAdhocPtpStat * socket = ptp[id - 1];
// Connected Socket
if (socket->state == ADHOC_PTP_STATE_ESTABLISHED) {
// Valid Arguments
if (data != NULL && len != NULL && *len > 0) {
// Schedule Timeout Removal
if (flag) timeout = 0;
// Apply Send Timeout Settings to Socket
setsockopt(socket->id, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout));
// Acquire Network Lock
// _acquireNetworkLock();
// Send Data
changeBlockingMode(socket->id, flag);
int sent = send(socket->id, data, *len, 0);
int error = errno;
changeBlockingMode(socket->id, 0);
// Free Network Lock
// _freeNetworkLock();
// Success
if (sent > 0) {
// Save Length
*len = sent;
uint8_t * smac = (uint8_t *)&socket->paddr;
INFO_LOG(SCENET, "sceNetAdhocPtpSend[%i:%u]: Sent %u bytes to %02X:%02X:%02X:%02X:%02X:%02X:%u", id, socket->lport, sent, smac[0], smac[1], smac[2], smac[3], smac[4], smac[5], socket->pport);
// Return Success
return 0;
}
// Non-Critical Error
else if (sent == -1 && error == EAGAIN) {
// Blocking Situation
if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK;
// Timeout
return ERROR_NET_ADHOC_TIMEOUT;
}
// Change Socket State
socket->state = ADHOC_PTP_STATE_CLOSED;
// Disconnected
return ERROR_NET_ADHOC_DISCONNECTED;
}
// Invalid Arguments
return ERROR_NET_ADHOC_INVALID_ARG;
}
// Not connected
return ERROR_NET_ADHOC_NOT_CONNECTED;
}
// Invalid Socket
return ERROR_NET_ADHOC_INVALID_SOCKET_ID;
}
// Library is uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
/**
* Adhoc Emulator PTP Receiver
* @param id Socket File Descriptor
* @param buf Data Buffer
* @param len IN: Buffersize OUT: Received Data (in Bytes)
* @param timeout Receive Timeout (in Microseconds)
* @param flag Nonblocking Flag
* @return 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_THREAD_ABORTED, ADHOC_DISCONNECTED, NET_INTERNAL
*/
static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeout, int flag) {
DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv(%d,%08x,%08x,%d,%d)", id, dataAddr, dataSizeAddr, timeout, flag);
if (!g_Config.bEnableWlan) {
return 0;
}
void * buf = (void *)Memory::GetPointer(dataAddr);
int * len = (int *)Memory::GetPointer(dataSizeAddr);
// Library is initialized
if (netAdhocInited) {
// Valid Socket
if (id > 0 && id <= 255 && ptp[id - 1] != NULL && ptp[id - 1]->state == ADHOC_PTP_STATE_ESTABLISHED) {
// Cast Socket
SceNetAdhocPtpStat * socket = ptp[id - 1];
// Valid Arguments
if (buf != NULL && len != NULL && *len > 0) {
// Schedule Timeout Removal
if (flag) timeout = 0;
// Apply Send Timeout Settings to Socket
setsockopt(socket->id, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout));
// Acquire Network Lock
// _acquireNetworkLock();
// Receive Data
changeBlockingMode(socket->id, flag);
int received = recv(socket->id, (char *)buf, *len, 0);
int error = errno;
changeBlockingMode(socket->id, 0);
// Free Network Lock
// _freeNetworkLock();
// Received Data
if (received > 0) {
// Save Length
*len = received;
uint8_t * smac = (uint8_t *)&socket->paddr;
INFO_LOG(SCENET, "sceNetAdhocPtpRecv[%i:%u]: Received %u bytes from %02X:%02X:%02X:%02X:%02X:%02X:%u", id, socket->lport, received, smac[0], smac[1], smac[2], smac[3], smac[4], smac[5], socket->pport);
// Return Success
return 0;
}
// Non-Critical Error
else if (received == -1 && error == EAGAIN) {
// Blocking Situation
if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK;
// Timeout
return ERROR_NET_ADHOC_TIMEOUT;
}
// Change Socket State
socket->state = ADHOC_PTP_STATE_CLOSED;
// Disconnected
return ERROR_NET_ADHOC_DISCONNECTED;
}
// Invalid Arguments
return ERROR_NET_ADHOC_INVALID_ARG;
}
// Invalid Socket
return ERROR_NET_ADHOC_INVALID_SOCKET_ID;
}
// Library is uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
/**
* Adhoc Emulator PTP Flusher
* @param id Socket File Descriptor
* @param timeout Flush Timeout (in Microseconds)
* @param flag Nonblocking Flag
* @return 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_THREAD_ABORTED, ADHOC_DISCONNECTED, ADHOC_NOT_CONNECTED, NET_INTERNAL
*/
static int sceNetAdhocPtpFlush(int id, int timeout, int nonblock) {
DEBUG_LOG(SCENET,"sceNetAdhocPtpFlush(%d,%d,%d) at %08x", id, timeout, nonblock, currentMIPS->pc);
if (!g_Config.bEnableWlan) {
return 0;
}
// Library initialized
if (netAdhocInited) {
// Valid Socket
if (id > 0 && id <= 255 && ptp[id - 1] != NULL) {
// Dummy Result
return 0;
}
// Invalid Socket
return ERROR_NET_ADHOC_INVALID_SOCKET_ID;
}
// Library uninitialized
return ERROR_NET_ADHOC_NOT_INITIALIZED;
}
static int sceNetAdhocGameModeCreateMaster(u32 data, int size) {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGameModeCreateMaster(%08x, %i) at %08x", data, size, currentMIPS->pc);
return -1;
}
static int sceNetAdhocGameModeCreateReplica(const char *mac, u32 data, int size) {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGameModeCreateReplica(%s, %08x, %i) at %08x", mac, data, size, currentMIPS->pc);
return -1;
}
static int sceNetAdhocGameModeUpdateMaster() {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGameModeUpdateMaster()");
return -1;
}
static int sceNetAdhocGameModeDeleteMaster() {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGameModeDeleteMaster()");
return -1;
}
static int sceNetAdhocGameModeUpdateReplica(int id, u32 infoAddr) {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGameModeUpdateReplica(%i, %08x)", id, infoAddr);
return -1;
}
static int sceNetAdhocGameModeDeleteReplica(int id) {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGameModeDeleteReplica(%i)", id);
return -1;
}
int sceNetAdhocGetSocketAlert(int id, u32 flagPtr) {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGetSocketAlert(%i, %08x)", id, flagPtr);
// Dummy Value
if (Memory::IsValidAddress(flagPtr)) {
s32_le * flag = (s32_le*)Memory::GetPointer(flagPtr);
*flag = 0;
}
// Dummy Result
return 0;
}
int sceNetAdhocMatchingStop(int matchingId) {
WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingStop(%i) at %08x", matchingId, currentMIPS->pc);
//if (!g_Config.bEnableWlan)
// return -1;
SceNetAdhocMatchingContext * item = findMatchingContext(matchingId);
if (item != NULL) {
item->inputRunning = false;
if (item->inputThread.joinable()) {
item->inputThread.join();
}
item->eventRunning = false;
if (item->eventThread.joinable()) {
item->eventThread.join();
}
// Stop fake PSP Thread
//__KernelStopThread(item->matching_thid, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching stopped");
//item->matchingThread->Terminate();
// Multithreading Lock
peerlock.lock();
// Remove your own MAC, or All memebers, or don't remove at all or we should do this on MatchingDelete ?
clearPeerList(item); //deleteAllMembers(item);
item->running = 0;
netAdhocMatchingStarted--;
// Multithreading Unlock
peerlock.unlock();
}
return 0;
}
int sceNetAdhocMatchingDelete(int matchingId) {
// WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right?
// Previous Context Reference
SceNetAdhocMatchingContext * prev = NULL;
// Multithreading Lock
peerlock.lock(); //contextlock.lock();
// Context Pointer
SceNetAdhocMatchingContext * item = contexts;
// Iterate contexts
for (; item != NULL; item = item->next) {
// Found matching ID
if (item->id == matchingId) {
// Unlink Left (Beginning)
if (prev == NULL) contexts = item->next;
// Unlink Left (Other)
else prev->next = item->next;
// Stop it first if it's still running
if (item->running) {
sceNetAdhocMatchingStop(matchingId);
}
// Delete the Fake PSP Thread
//__KernelDeleteThread(item->matching_thid, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching deleted");
//delete item->matchingThread;
// Make sure nobody locking/using the socket
item->socketlock->lock();
// Delete the socket
sceNetAdhocPdpDelete(item->socket, 0); // item->connected = (sceNetAdhocPdpDelete(item->socket, 0) < 0);
item->socketlock->unlock();
// Free allocated memories
free(item->hello);
free(item->rxbuf);
clearPeerList(item); //deleteAllMembers(item);
// Destroy locks
item->eventlock->lock(); // Make sure it's not locked when being deleted
item->eventlock->unlock();
delete item->eventlock;
item->inputlock->lock(); // Make sure it's not locked when being deleted
item->inputlock->unlock();
delete item->inputlock;
item->socketlock->lock(); // Make sure it's not locked when being deleted
item->socketlock->unlock();
delete item->socketlock;
// Free item context memory
free(item);
item = NULL;
// Stop Search
break;
}
// Set Previous Reference
prev = item;
}
// Multithreading Unlock
peerlock.unlock(); //contextlock.unlock();
WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingDelete(%i) at %08x", matchingId, currentMIPS->pc);
return 0;
}
int sceNetAdhocMatchingInit(u32 memsize) {
WARN_LOG(SCENET, "sceNetAdhocMatchingInit(%d) at %08x", memsize, currentMIPS->pc);
// Uninitialized Library
if (netAdhocMatchingInited) return ERROR_NET_ADHOC_MATCHING_ALREADY_INITIALIZED;
// Save Fake Pool Size
fakePoolSize = memsize;
// Initialize Library
netAdhocMatchingInited = true;
// Return Success
return 0;
}
int sceNetAdhocMatchingTerm() {
// Should we cleanup all created matching contexts first? just in case there are games that doesn't delete them before calling this
if (netAdhocMatchingInited) {
// Delete all Matching contexts
SceNetAdhocMatchingContext * next = NULL;
SceNetAdhocMatchingContext * context = contexts;
while (context != NULL) {
next = context->next;
if (context->running) sceNetAdhocMatchingStop(context->id);
sceNetAdhocMatchingDelete(context->id);
context = next;
}
}
WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingTerm()");
netAdhocMatchingInited = false;
return 0;
}
// Presumably returns a "matchingId".
static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbuflen, int hello_int, int keepalive_int, int init_count, int rexmt_int, u32 callbackAddr) {
WARN_LOG(SCENET, "sceNetAdhocMatchingCreate(mode=%i, maxnum=%i, port=%i, rxbuflen=%i, hello=%i, keepalive=%i, initcount=%i, rexmt=%i, callbackAddr=%08x) at %08x", mode, maxnum, port, rxbuflen, hello_int, keepalive_int, init_count, rexmt_int, callbackAddr, currentMIPS->pc);
if (!g_Config.bEnableWlan) {
return -1;
}
SceNetAdhocMatchingHandler handler;
handler.entryPoint = callbackAddr;
// Library initialized
if (netAdhocMatchingInited) {
// Valid Member Limit
if (maxnum > 1 && maxnum <= 16) {
// Valid Receive Buffer size
if (rxbuflen >= 1024) {
// Valid Arguments
if (mode >= 1 && mode <= 3) {
// Iterate Matching Contexts
SceNetAdhocMatchingContext * item = contexts; for (; item != NULL; item = item->next) {
// Port Match found
if (item->port == port) return ERROR_NET_ADHOC_MATCHING_PORT_IN_USE;
}
// Allocate Context Memory
SceNetAdhocMatchingContext * context = (SceNetAdhocMatchingContext *)malloc(sizeof(SceNetAdhocMatchingContext));
// Allocated Memory
if (context != NULL) {
// Create PDP Socket
SceNetEtherAddr localmac; getLocalMac(&localmac);
const char * mac = (const char *)&localmac.data;
int socket = sceNetAdhocPdpCreate(mac, (uint32_t)port, rxbuflen, 0);
// Created PDP Socket
if (socket > 0) {
// Clear Memory
memset(context, 0, sizeof(SceNetAdhocMatchingContext));
// Allocate Receive Buffer
context->rxbuf = (uint8_t *)malloc(rxbuflen);
// Allocated Memory
if (context->rxbuf != NULL) {
// Clear Memory
memset(context->rxbuf, 0, rxbuflen);
// Fill in Context Data
context->id = findFreeMatchingID();
context->mode = mode;
context->maxpeers = maxnum;
context->port = port;
context->socket = socket;
context->rxbuflen = rxbuflen;
context->resendcounter = init_count;
context->resend_int = rexmt_int; // used as ack timeout on lost packet (ie. not receiving anything after sending)?
context->hello_int = hello_int; // client might set this to 0
if (keepalive_int < 1) context->keepalive_int = PSP_ADHOCCTL_PING_TIMEOUT; else context->keepalive_int = keepalive_int; // client might set this to 0
context->keepalivecounter = init_count; // used to multiply keepalive_int as timeout
context->timeout = (keepalive_int * init_count);
if (context->timeout < 5000000) context->timeout = 5000000; // For internet play we need higher timeout than what the game wanted
context->handler = handler;
// Fill in Selfpeer
context->mac = localmac;
// Create locks
context->socketlock = new recursive_mutex;
context->eventlock = new recursive_mutex;
context->inputlock = new recursive_mutex;
// Create fake thread
//#define PSP_THREAD_ATTR_KERNEL 0x00001000 // PSP_THREAD_ATTR_KERNEL is located in sceKernelThread.cpp instead of sceKernelThread.h :(
//context->matching_thid = __KernelCreateThreadInternal("AdhocMatching", 0, idleThreadHackAddr, 0x7f, 4096, PSP_THREAD_ATTR_KERNEL);
//context->matchingThread = new HLEHelperThread("AdhocMatching", "AdhocMatchingMod", "AdhocMatchingFunc", 0x7f, 4096); //8192
// Multithreading Lock
peerlock.lock(); //contextlock.lock();
// Add Callback Handler
context->handler.entryPoint = callbackAddr;
// Link Context
//context->connected = true;
context->next = contexts;
contexts = context;
// Multithreading UnLock
peerlock.unlock(); //contextlock.unlock();
// Return Matching ID
return context->id;
}
// Close PDP Socket
sceNetAdhocPdpDelete(socket, 0); // context->connected = (sceNetAdhocPdpDelete(socket, 0) < 0);
}
// Free Memory
free(context);
// Port in use
if (socket < 1) return ERROR_NET_ADHOC_MATCHING_PORT_IN_USE; // ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED; // -1; // ERROR_NET_ADHOC_MATCHING_NOT_ESTABLISHED;
}
// Out of Memory
return ERROR_NET_ADHOC_MATCHING_NO_SPACE;
}
// InvalidERROR_NET_Arguments
return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
}
// Invalid Receive Buffer Size
return ERROR_NET_ADHOC_MATCHING_RXBUF_TOO_SHORT;
}
// Invalid Member Limit
return ERROR_NET_ADHOC_MATCHING_INVALID_MAXNUM;
}
// Uninitialized Library
return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED;
}
// TODO: Should we execute the callback used to create the Matching Id if it's a valid address?
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;
}*/
// Start the Fake PSP Thread
//__KernelStartThread(item->matching_thid, 0, 0);
//item->matchingThread->Start(0, 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();
return 0;
}
static int sceNetAdhocMatchingSelectTarget(int matchingId, const char *macAddress, int optLen, u32 optDataPtr) {
WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingSelectTarget(%i, %s, %i, %08x) at %08x", matchingId, macAddress, optLen, optDataPtr, currentMIPS->pc);
if (!g_Config.bEnableWlan)
return -1;
// Initialized Library
if (netAdhocMatchingInited)
{
// Valid Arguments
if (macAddress != NULL)
{
SceNetEtherAddr * target = (SceNetEtherAddr *)macAddress;
// Find Matching Context for ID
SceNetAdhocMatchingContext * context = findMatchingContext(matchingId);
// Found Matching Context
if (context != NULL)
{
// Running Context
if (context->running)
{
// Search Result
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, (SceNetEtherAddr *)target);
// Found Peer in List
if (peer != NULL)
{
// Valid Optional Data Length
if ((optLen == 0 && optDataPtr == NULL) || (optLen > 0 && optDataPtr != NULL))
{
void * opt = NULL;
if (Memory::IsValidAddress(optDataPtr)) opt = Memory::GetPointer(optDataPtr);
// Host Mode
if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT)
{
// Already Connected
if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) return ERROR_NET_ADHOC_MATCHING_ALREADY_ESTABLISHED;
// Not enough space
if (countChildren(context) == (context->maxpeers - 1)) return ERROR_NET_ADHOC_MATCHING_EXCEED_MAXNUM;
// Requesting Peer
if (peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST)
{
// Accept Peer in Group
peer->state = PSP_ADHOC_MATCHING_PEER_CHILD;
// Sending order may need to be reversed since Stack appends to the front, so the order will be switched around.
// Tell Children about new Sibling
sendBirthMessage(context, peer);
// Spawn Established Event
//spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_ESTABLISHED, target, 0, NULL);
// Send Accept Confirmation to Peer
sendAcceptMessage(context, peer, optLen, opt);
// Return Success
return 0;
}
}
// Client Mode
else if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD)
{
// Already connected
if (findParent(context) != NULL) return ERROR_NET_ADHOC_MATCHING_ALREADY_ESTABLISHED;
// Outgoing Request in Progress
if (findOutgoingRequest(context) != NULL) return ERROR_NET_ADHOC_MATCHING_REQUEST_IN_PROGRESS;
// Valid Offer
if (peer->state == PSP_ADHOC_MATCHING_PEER_OFFER)
{
// Switch into Join Request Mode
peer->state = PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST;
// Send Join Request to Peer
sendJoinRequest(context, peer, optLen, opt);
// Return Success
return 0;
}
}
// P2P Mode
else
{
// Already connected
if (findP2P(context) != NULL) return ERROR_NET_ADHOC_MATCHING_ALREADY_ESTABLISHED;
// Outgoing Request in Progress
if (findOutgoingRequest(context) != NULL) return ERROR_NET_ADHOC_MATCHING_REQUEST_IN_PROGRESS;
// Join Request Mode
if (peer->state == PSP_ADHOC_MATCHING_PEER_OFFER)
{
// Switch into Join Request Mode
peer->state = PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST;
// Send Join Request to Peer
sendJoinRequest(context, peer, optLen, opt);
// Return Success
return 0;
}
// Requesting Peer
else if (peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST)
{
// Accept Peer in Group
peer->state = PSP_ADHOC_MATCHING_PEER_P2P;
// Tell Children about new Sibling
//sendBirthMessage(context, peer);
// Send Accept Confirmation to Peer
sendAcceptMessage(context, peer, optLen, opt);
// Return Success
return 0;
}
}
// How did this happen?! It shouldn't!
return ERROR_NET_ADHOC_MATCHING_TARGET_NOT_READY;
}
// Invalid Optional Data Length
return ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN;
}
// Peer not found
return ERROR_NET_ADHOC_MATCHING_UNKNOWN_TARGET;
}
// Idle Context
return ERROR_NET_ADHOC_MATCHING_NOT_RUNNING;
}
// Invalid Matching ID
return ERROR_NET_ADHOC_MATCHING_INVALID_ID;
}
// Invalid Arguments
return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
}
// Uninitialized Library
return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED;
}
int sceNetAdhocMatchingCancelTargetWithOpt(int matchingId, const char *macAddress, int optLen, u32 optDataPtr) {
WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingCancelTargetWithOpt(%i, %s, %i, %08x) at %08x", matchingId, macAddress, optLen, optDataPtr, currentMIPS->pc);
if (!g_Config.bEnableWlan)
return -1;
// Initialized Library
if (netAdhocMatchingInited)
{
SceNetEtherAddr * target = (SceNetEtherAddr *)macAddress;
void * opt = NULL;
if (Memory::IsValidAddress(optDataPtr)) opt = Memory::GetPointer(optDataPtr);
// Valid Arguments
if (target != NULL && ((optLen == 0 && opt == NULL) || (optLen > 0 && opt != NULL)))
{
// Find Matching Context
SceNetAdhocMatchingContext * context = findMatchingContext(matchingId);
// Found Matching Context
if (context != NULL)
{
// Running Context
if (context->running)
{
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, (SceNetEtherAddr *)target);
// Found Peer
if (peer != NULL)
{
// Valid Peer Mode
if ((context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && (peer->state == PSP_ADHOC_MATCHING_PEER_PARENT || peer->state == PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST)) ||
(context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD || peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST)) ||
(context->mode == PSP_ADHOC_MATCHING_MODE_P2P && (peer->state == PSP_ADHOC_MATCHING_PEER_P2P || peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST)))
{
// Notify other Children of Death
if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && peer->state == PSP_ADHOC_MATCHING_PEER_CHILD && countConnectedPeers(context) > 1)
{
// Send Death Message
sendDeathMessage(context, peer);
}
// Mark Peer as Canceled
peer->state = PSP_ADHOC_MATCHING_PEER_CANCEL_IN_PROGRESS;
// Send Cancel Event to Peer
sendCancelMessage(context, peer, optLen, opt);
// Delete Peer from List
// Can't delete here, Threads still need this data.
// deletePeer(context, peer);
// Return Success
return 0;
}
}
// Peer not found
//return ERROR_NET_ADHOC_MATCHING_UNKNOWN_TARGET;
// Faking success to prevent the game (ie. Soul Calibur) to repeatedly calling this function when the other player is disconnected
return 0;
}
// Context not running
return ERROR_NET_ADHOC_MATCHING_NOT_RUNNING;
}
// Invalid Matching ID
return ERROR_NET_ADHOC_MATCHING_INVALID_ID;
}
// Invalid Arguments
return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
}
// Uninitialized Library
return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED;
}
int sceNetAdhocMatchingCancelTarget(int matchingId, const char *macAddress) {
WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingCancelTarget(%i, %s)", matchingId, macAddress);
if (!g_Config.bEnableWlan)
return -1;
return sceNetAdhocMatchingCancelTargetWithOpt(matchingId, macAddress, 0, NULL);
}
int sceNetAdhocMatchingGetHelloOpt(int matchingId, u32 optLenAddr, u32 optDataAddr) {
WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingGetHelloOpt(%i, %08x, %08x)", matchingId, optLenAddr, optDataAddr);
if (!g_Config.bEnableWlan)
return -1;
if (!Memory::IsValidAddress(optLenAddr)) return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
s32_le * optlen = (s32_le *)optLenAddr;
// Multithreading Lock
peerlock.lock();
SceNetAdhocMatchingContext * item = findMatchingContext(matchingId);
if (item != NULL) {
// Get OptData
*optlen = item->hellolen;
if ((*optlen > 0) && Memory::IsValidAddress(optDataAddr)) {
uint8_t * optdata = Memory::GetPointer(optDataAddr);
memcpy(optdata, item->hello, *optlen);
}
//else return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
}
//else return ERROR_NET_ADHOC_MATCHING_INVALID_ID;
// Multithreading Unlock
peerlock.unlock();
return 0;
}
int sceNetAdhocMatchingSetHelloOpt(int matchingId, int optLenAddr, u32 optDataAddr) {
DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocMatchingSetHelloOpt(%i, %i, %08x) at %08x", matchingId, optLenAddr, optDataAddr, currentMIPS->pc);
if (!g_Config.bEnableWlan)
return -1;
if (!netAdhocMatchingInited) return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED;
// Multithreading Lock
peerlock.lock();
SceNetAdhocMatchingContext * context = findMatchingContext(matchingId);
// Multithreading Unlock
peerlock.unlock();
// Found Context
if (context != NULL)
{
// Valid Matching Modes
if (context->mode != PSP_ADHOC_MATCHING_MODE_CHILD)
{
// Running Context
if (context->running)
{
// Valid Optional Data Length
if ((optLenAddr == 0 && optDataAddr == NULL) || (optLenAddr > 0 && optDataAddr != NULL))
{
// Grab Existing Hello Data
void * hello = context->hello;
// Free Previous Hello Data, or Reuse it
//free(hello);
// Allocation Required
if (optLenAddr > 0)
{
// Allocate Memory
if (optLenAddr > context->hellolen) {
hello = realloc(hello, optLenAddr);
}
// Out of Memory
if (hello == NULL) {
context->hellolen = 0;
return ERROR_NET_ADHOC_MATCHING_NO_SPACE;
}
// Clone Hello Data
//memcpy(hello, opt, optLenAddr);
Memory::Memcpy(hello, optDataAddr, optLenAddr);
// Set Hello Data
context->hello = (uint8_t*)hello;
context->hellolen = optLenAddr;
context->helloAddr = optDataAddr;
}
else
{
// Delete Hello Data
context->hellolen = 0;
context->helloAddr = 0;
//free(context->hello); // Doesn't need to free it since it will be reused later
//context->hello = NULL;
}
// Return Success
return 0;
}
// Invalid Optional Data Length
return ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN;
}
// Context not running
return ERROR_NET_ADHOC_MATCHING_NOT_RUNNING;
}
// Invalid Matching Mode (Child)
return ERROR_NET_ADHOC_MATCHING_INVALID_MODE;
}
return 0;
}
static int sceNetAdhocMatchingGetMembers(int matchingId, u32 sizeAddr, u32 buf) {
DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocMatchingGetMembers(%i, [%08x]=%i, %08x) at %08x", matchingId, sizeAddr, Memory::Read_U32(sizeAddr), buf, currentMIPS->pc);
if (!g_Config.bEnableWlan)
return -1;
if (!netAdhocMatchingInited) return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED;
// Minimum Argument
if (!Memory::IsValidAddress(sizeAddr)) return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
// Multithreading Lock
peerlock.lock();
// Find Matching Context
SceNetAdhocMatchingContext * context = findMatchingContext(matchingId);
// Multithreading Unlock
peerlock.unlock();
// Found Context
if (context != NULL)
{
// Running Context
if (context->running)
{
// Length Buffer available
if (sizeAddr != NULL)
{
int * buflen = (int *)Memory::GetPointer(sizeAddr);
SceNetAdhocMatchingMemberInfoEmu * buf2 = NULL;
if (Memory::IsValidAddress(buf)) {
buf2 = (SceNetAdhocMatchingMemberInfoEmu *)Memory::GetPointer(buf);
}
// Number of Connected Peers
uint32_t peercount = countConnectedPeers(context);
// Calculate Connected Peer Bytesize
int available = sizeof(SceNetAdhocMatchingMemberInfoEmu) * peercount;
// Length Returner Mode
if (buf == NULL)
{
// Get Connected Peer Count
*buflen = available;
}
// Normal Mode
else
{
// Fix Negative Length
if ((*buflen) < 0) *buflen = 0;
// Fix Oversize Request
if ((*buflen) > available) *buflen = available;
// Clear Memory
memset(buf2, 0, *buflen);
// Calculate Requested Peer Count
int requestedpeers = (*buflen) / sizeof(SceNetAdhocMatchingMemberInfoEmu);
// Filled Request Counter
int filledpeers = 0;
// Add Self-Peer
if (requestedpeers > 0)
{
// Add Local MAC
buf2[filledpeers++].mac_addr = context->mac;
// Room for more than local peer
if (requestedpeers > 1)
{
// P2P Mode
if (context->mode == PSP_ADHOC_MATCHING_MODE_P2P)
{
// Find P2P Brother
SceNetAdhocMatchingMemberInternal * p2p = findP2P(context);
// P2P Brother found
if (p2p != NULL)
{
// Add P2P Brother MAC
buf2[filledpeers++].mac_addr = p2p->mac;
}
}
// Parent or Child Mode
else
{
// Iterate Peer List
SceNetAdhocMatchingMemberInternal * peer = context->peerlist; for (; peer != NULL && filledpeers < requestedpeers; peer = peer->next)
{
// Parent Mode
if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT)
{
// Interested in Children (Michael Jackson Style)
if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD)
{
// Add Child MAC
buf2[filledpeers++].mac_addr = peer->mac;
}
}
// Children Mode
else
{
// Interested in Parent & Siblings
if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD || peer->state == PSP_ADHOC_MATCHING_PEER_PARENT)
{
// Add Peer MAC
buf2[filledpeers++].mac_addr = peer->mac;
}
}
}
}
}
// Link Result List
int i = 0;
for (; i < filledpeers - 1; i++)
{
// Link Next Element
//buf2[i].next = &buf2[i + 1];
buf2[i].next = buf + (sizeof(SceNetAdhocMatchingMemberInfoEmu)*i) + sizeof(SceNetAdhocMatchingMemberInfoEmu);
}
// Fix Last Element
if (filledpeers > 0) buf2[filledpeers - 1].next = 0;
}
// Fix Buffer Size
*buflen = sizeof(SceNetAdhocMatchingMemberInfoEmu) * filledpeers;
}
// Return Success
return 0;
}
// Invalid Arguments
return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
}
// Context not running
return ERROR_NET_ADHOC_MATCHING_NOT_RUNNING;
}
// Invalid Matching ID
return ERROR_NET_ADHOC_MATCHING_INVALID_ID;
}
int sceNetAdhocMatchingSendData(int matchingId, const char *mac, int dataLen, u32 dataAddr) {
WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingSendData(%i, %s, %i, %08x)", matchingId, mac, dataLen, dataAddr);
if (!g_Config.bEnableWlan)
return -1;
// Initialized Library
if (netAdhocMatchingInited)
{
// Valid Arguments
if (mac != NULL)
{
// Find Matching Context
SceNetAdhocMatchingContext * context = findMatchingContext(matchingId);
// Found Context
if (context != NULL)
{
// Running Context
if (context->running)
{
// Find Target Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, (SceNetEtherAddr *)mac);
// Found Peer
if (peer != NULL)
{
void * data = NULL;
if (Memory::IsValidAddress(dataAddr)) data = Memory::GetPointer(dataAddr);
// Valid Data Length
if (dataLen > 0 && data != NULL)
{
// Valid Peer Connection State
if (peer->state == PSP_ADHOC_MATCHING_PEER_PARENT || peer->state == PSP_ADHOC_MATCHING_PEER_CHILD || peer->state == PSP_ADHOC_MATCHING_PEER_P2P)
{
// Send in Progress
if (peer->sending) return ERROR_NET_ADHOC_MATCHING_DATA_BUSY;
// Mark Peer as Sending
peer->sending = 1;
// Send Data to Peer
sendBulkData(context, peer, dataLen, data);
// Return Success
return 0;
}
// Not connected / accepted
return ERROR_NET_ADHOC_MATCHING_NOT_ESTABLISHED;
}
// Invalid Data Length
return ERROR_NET_ADHOC_MATCHING_INVALID_DATALEN;
}
// Peer not found
return ERROR_NET_ADHOC_MATCHING_UNKNOWN_TARGET;
}
// Context not running
return ERROR_NET_ADHOC_MATCHING_NOT_RUNNING;
}
// Invalid Matching ID
return ERROR_NET_ADHOC_MATCHING_INVALID_ID;
}
// Invalid Arguments
return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
}
// Uninitialized Library
return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED;
}
int sceNetAdhocMatchingAbortSendData(int matchingId, const char *mac) {
WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingAbortSendData(%i, %s)", matchingId, mac);
if (!g_Config.bEnableWlan)
return -1;
// Initialized Library
if (netAdhocMatchingInited)
{
// Valid Arguments
if (mac != NULL)
{
// Find Matching Context
SceNetAdhocMatchingContext * context = findMatchingContext(matchingId);
// Found Context
if (context != NULL)
{
// Running Context
if (context->running)
{
// Find Target Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, (SceNetEtherAddr *)mac);
// Found Peer
if (peer != NULL)
{
// Peer is sending
if (peer->sending)
{
// Set Peer as Bulk Idle
peer->sending = 0;
// Stop Bulk Data Sending (if in progress)
abortBulkTransfer(context, peer);
}
// Return Success
return 0;
}
// Peer not found
return ERROR_NET_ADHOC_MATCHING_UNKNOWN_TARGET;
}
// Context not running
return ERROR_NET_ADHOC_MATCHING_NOT_RUNNING;
}
// Invalid Matching ID
return ERROR_NET_ADHOC_MATCHING_INVALID_ID;
}
// Invalid Arguments
return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
}
// Uninitialized Library
return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED;
}
static int sceNetAdhocMatchingGetPoolMaxAlloc() {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocMatchingGetPoolMaxAlloc()");
if (!g_Config.bEnableWlan)
return -1;
// Lazy way out - hardcoded return value
return (50 * 1024);
}
int sceNetAdhocMatchingGetPoolStat(u32 poolstatPtr) {
WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingGetPoolStat(%08x)", poolstatPtr);
if (!g_Config.bEnableWlan)
return -1;
// Initialized Library
if (netAdhocMatchingInited)
{
SceNetMallocStat * poolstat = NULL;
if (Memory::IsValidAddress(poolstatPtr)) poolstat = (SceNetMallocStat *)Memory::GetPointer(poolstatPtr);
// Valid Argument
if (poolstat != NULL)
{
// Fill Poolstat with Fake Data
poolstat->pool = fakePoolSize;
poolstat->maximum = fakePoolSize / 8 * 6;
poolstat->free = fakePoolSize / 8 * 7;
// Return Success
return 0;
}
// Invalid Argument
return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
}
// Uninitialized Library
return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED;
}
const HLEFunction sceNetAdhoc[] = {
{0xE1D621D7, WrapU_V<sceNetAdhocInit>, "sceNetAdhocInit"},
{0xA62C6F57, WrapI_V<sceNetAdhocTerm>, "sceNetAdhocTerm"},
{0x0AD043ED, WrapI_U<sceNetAdhocctlConnect>, "sceNetAdhocctlConnect"},
{0x6f92741b, WrapI_CUIU<sceNetAdhocPdpCreate>, "sceNetAdhocPdpCreate"},
{0xabed3790, WrapI_ICUVIII<sceNetAdhocPdpSend>, "sceNetAdhocPdpSend"},
{0xdfe53e03, WrapI_IVVVVUI<sceNetAdhocPdpRecv>, "sceNetAdhocPdpRecv"},
{0x7f27bb5e, WrapI_II<sceNetAdhocPdpDelete>, "sceNetAdhocPdpDelete"},
{0xc7c1fc57, WrapI_UU<sceNetAdhocGetPdpStat>, "sceNetAdhocGetPdpStat"},
{0x157e6225, WrapI_II<sceNetAdhocPtpClose>, "sceNetAdhocPtpClose"},
{0x4da4c788, WrapI_IUUII<sceNetAdhocPtpSend>, "sceNetAdhocPtpSend"},
{0x877f6d66, WrapI_CICIIIII<sceNetAdhocPtpOpen>, "sceNetAdhocPtpOpen"},
{0x8bea2b3e, WrapI_IUUII<sceNetAdhocPtpRecv>, "sceNetAdhocPtpRecv"},
{0x9df81198, WrapI_IUUII<sceNetAdhocPtpAccept>, "sceNetAdhocPtpAccept"},
{0xe08bdac1, WrapI_CIIIIII<sceNetAdhocPtpListen>, "sceNetAdhocPtpListen"},
{0xfc6fc07b, WrapI_III<sceNetAdhocPtpConnect>, "sceNetAdhocPtpConnect"},
{0x9ac2eeac, WrapI_III<sceNetAdhocPtpFlush>, "sceNetAdhocPtpFlush"},
{0xb9685118, WrapI_UU<sceNetAdhocGetPtpStat>, "sceNetAdhocGetPtpStat"},
{0x3278ab0c, WrapI_CUI<sceNetAdhocGameModeCreateReplica>, "sceNetAdhocGameModeCreateReplica"},
{0x98c204c8, WrapI_V<sceNetAdhocGameModeUpdateMaster>, "sceNetAdhocGameModeUpdateMaster"},
{0xfa324b4e, WrapI_IU<sceNetAdhocGameModeUpdateReplica>, "sceNetAdhocGameModeUpdateReplica"},
{0xa0229362, WrapI_V<sceNetAdhocGameModeDeleteMaster>, "sceNetAdhocGameModeDeleteMaster"},
{0x0b2228e9, WrapI_I<sceNetAdhocGameModeDeleteReplica>, "sceNetAdhocGameModeDeleteReplica"},
{0x7F75C338, WrapI_UI<sceNetAdhocGameModeCreateMaster>, "sceNetAdhocGameModeCreateMaster"},
{0x73bfd52d, WrapI_II<sceNetAdhocSetSocketAlert>, "sceNetAdhocSetSocketAlert"},
{0x4d2ce199, WrapI_IU<sceNetAdhocGetSocketAlert>, "sceNetAdhocGetSocketAlert"},
{0x7a662d6b, WrapI_UIII<sceNetAdhocPollSocket>, "sceNetAdhocPollSocket"},
};
const HLEFunction sceNetAdhocMatching[] = {
{0x2a2a1e07, WrapI_U<sceNetAdhocMatchingInit>, "sceNetAdhocMatchingInit"},
{0x7945ecda, WrapI_V<sceNetAdhocMatchingTerm>, "sceNetAdhocMatchingTerm"},
{0xca5eda6f, WrapI_IIIIIIIIU<sceNetAdhocMatchingCreate>, "sceNetAdhocMatchingCreate"},
{0x93ef3843, WrapI_IIIIIIU<sceNetAdhocMatchingStart>, "sceNetAdhocMatchingStart"},
{0x32b156b3, WrapI_I<sceNetAdhocMatchingStop>, "sceNetAdhocMatchingStop"},
{0xf16eaf4f, WrapI_I<sceNetAdhocMatchingDelete>, "sceNetAdhocMatchingDelete"},
{0x5e3d4b79, WrapI_ICIU<sceNetAdhocMatchingSelectTarget>, "sceNetAdhocMatchingSelectTarget"},
{0xea3c6108, WrapI_IC<sceNetAdhocMatchingCancelTarget>, "sceNetAdhocMatchingCancelTarget"},
{0x8f58bedf, WrapI_ICIU<sceNetAdhocMatchingCancelTargetWithOpt>, "sceNetAdhocMatchingCancelTargetWithOpt"},
{0xb5d96c2a, WrapI_IUU<sceNetAdhocMatchingGetHelloOpt>, "sceNetAdhocMatchingGetHelloOpt"},
{0xb58e61b7, WrapI_IIU<sceNetAdhocMatchingSetHelloOpt>, "sceNetAdhocMatchingSetHelloOpt"},
{0xc58bcd9e, WrapI_IUU<sceNetAdhocMatchingGetMembers>, "sceNetAdhocMatchingGetMembers"},
{0xf79472d7, WrapI_ICIU<sceNetAdhocMatchingSendData>, "sceNetAdhocMatchingSendData"},
{0xec19337d, WrapI_IC<sceNetAdhocMatchingAbortSendData>, "sceNetAdhocMatchingAbortSendData"},
{0x40F8F435, WrapI_V<sceNetAdhocMatchingGetPoolMaxAlloc>, "sceNetAdhocMatchingGetPoolMaxAlloc"},
{0x9c5cfb7d, WrapI_U<sceNetAdhocMatchingGetPoolStat>, "sceNetAdhocMatchingGetPoolStat"},
};
static int sceNetAdhocctlExitGameMode() {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlExitGameMode()");
return 0;
}
static int sceNetAdhocctlGetGameModeInfo(u32 infoAddr) {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlGetGameModeInfo(%08x)", infoAddr);
return -1;
}
static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) {
s32_le *buflen = NULL;
if (Memory::IsValidAddress(sizeAddr)) buflen = (s32_le *)Memory::GetPointer(sizeAddr);
SceNetAdhocctlPeerInfoEmu *buf = NULL;
if (Memory::IsValidAddress(bufAddr)) buf = (SceNetAdhocctlPeerInfoEmu *)Memory::GetPointer(bufAddr);
DEBUG_LOG(SCENET, "sceNetAdhocctlGetPeerList([%08x]=%i, %08x) at %08x", sizeAddr, buflen ? *buflen : -1, bufAddr, currentMIPS->pc);
if (!g_Config.bEnableWlan) {
return -1;
}
// Initialized Library
if (netAdhocctlInited) {
// Minimum Arguments
if (buflen != NULL) {
// Multithreading Lock
peerlock.lock();
// Length Calculation Mode
if (buf == NULL) *buflen = getActivePeerCount() * sizeof(SceNetAdhocctlPeerInfoEmu);
// Normal Mode
else {
// Discovery Counter
int discovered = 0;
// Calculate Request Count
int requestcount = *buflen / sizeof(SceNetAdhocctlPeerInfoEmu);
// Clear Memory
memset(buf, 0, *buflen);
// Minimum Arguments
if (requestcount > 0) {
// Peer Reference
SceNetAdhocctlPeerInfo * peer = friends;
// Iterate Peers
for (; peer != NULL && discovered < requestcount; peer = peer->next) {
// Fake Receive Time
if (peer->last_recv != 0) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
// Copy Peer Info
buf[discovered].nickname = peer->nickname;
buf[discovered].mac_addr = peer->mac_addr;
buf[discovered].ip_addr = peer->ip_addr;
buf[discovered].last_recv = peer->last_recv;
discovered++;
}
// Link List
int i = 0; for (; i < discovered - 1; i++) {
// Link Network
buf[i].next = bufAddr+(sizeof(SceNetAdhocctlPeerInfoEmu)*i) + sizeof(SceNetAdhocctlPeerInfoEmu);
}
// Fix Last Element
if (discovered > 0) buf[discovered - 1].next = 0;
}
// Fix Size
*buflen = discovered * sizeof(SceNetAdhocctlPeerInfoEmu);
}
// Multithreading Unlock
peerlock.unlock();
// Return Success
return 0;
}
// Invalid Arguments
return ERROR_NET_ADHOCCTL_INVALID_ARG;
}
// Uninitialized Library
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
}
static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 bufAddr) {
s32_le *buflen = NULL; //int32_t
if (Memory::IsValidAddress(sizeAddr)) buflen = (s32_le *)Memory::GetPointer(sizeAddr);
WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlGetPeerList(%s, [%08x]=%i, %08x)", nickName, sizeAddr, buflen ? *buflen : -1, bufAddr);
// Library initialized
if (netAdhocctlInited)
{
// Valid Arguments
if (nickName != NULL && buflen != NULL)
{
SceNetAdhocctlPeerInfoEmu *buf = NULL;
if (Memory::IsValidAddress(bufAddr)) buf = (SceNetAdhocctlPeerInfoEmu *)Memory::GetPointer(bufAddr);
// Multithreading Lock
peerlock.lock();
// Length Calculation Mode
if (buf == NULL) *buflen = getNicknameCount(nickName) * sizeof(SceNetAdhocctlPeerInfoEmu);
// Normal Information Mode
else
{
// Clear Memory
memset(buf, 0, *buflen);
// Discovered Player Count
int discovered = 0;
// Calculate Requested Elements
int requestcount = *buflen / sizeof(SceNetAdhocctlPeerInfoEmu);
// Minimum Space available
if (requestcount > 0)
{
// Local Nickname Matches
if (strcmp((char *)parameter.nickname.data, nickName) == 0)
{
// Get Local IP Address
sockaddr_in addr;
getLocalIp(&addr);
//buf->next = 0;
buf[discovered].nickname = parameter.nickname;
buf[discovered].nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char
getLocalMac(&buf[discovered].mac_addr);
buf[discovered].ip_addr = addr.sin_addr.s_addr; // 0x11111111;
//buf->padding = 0x1111; //0;
buf[discovered++].last_recv = CoreTiming::GetGlobalTimeUsScaled();
}
// Peer Reference
SceNetAdhocctlPeerInfo * peer = friends;
// Iterate Peers
for (; peer != NULL && discovered < requestcount; peer = peer->next)
{
// Match found
if (strcmp((char *)peer->nickname.data, nickName) == 0)
{
// Fake Receive Time
if (peer->last_recv != 0) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled(); //sceKernelGetSystemTimeWide();
// Copy Peer Info
buf[discovered].nickname = peer->nickname;
buf[discovered].nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char
buf[discovered].mac_addr = peer->mac_addr;
buf[discovered].ip_addr = peer->ip_addr;
buf[discovered++].last_recv = peer->last_recv;
}
}
// Link List
int i = 0; for (; i < discovered - 1; i++)
{
// Link Network
buf[i].next = bufAddr + (sizeof(SceNetAdhocctlPeerInfoEmu)*i) + sizeof(SceNetAdhocctlPeerInfoEmu);
}
// Fix Last Element
if (discovered > 0) buf[discovered - 1].next = 0;
}
// Fix Buffer Size
*buflen = discovered * sizeof(SceNetAdhocctlPeerInfoEmu);
}
// Multithreading Unlock
peerlock.unlock();
// Return Success
return 0;
}
// Invalid Arguments
return ERROR_NET_ADHOCCTL_INVALID_ARG;
}
// Library uninitialized
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
}
const HLEFunction sceNetAdhocctl[] = {
{0xE26F226E, WrapU_IIU<sceNetAdhocctlInit>, "sceNetAdhocctlInit"},
{0x9D689E13, WrapI_V<sceNetAdhocctlTerm>, "sceNetAdhocctlTerm"},
{0x20B317A0, WrapU_UU<sceNetAdhocctlAddHandler>, "sceNetAdhocctlAddHandler"},
{0x6402490B, WrapU_U<sceNetAdhocctlDelHandler>, "sceNetAdhocctlDelHandler"},
{0x34401D65, WrapU_V<sceNetAdhocctlDisconnect>, "sceNetAdhocctlDisconnect"},
{0x0ad043ed, WrapI_U<sceNetAdhocctlConnect>, "sceNetAdhocctlConnect"},
{0x08fff7a0, WrapI_V<sceNetAdhocctlScan>, "sceNetAdhocctlScan"},
{0x75ecd386, WrapI_U<sceNetAdhocctlGetState>, "sceNetAdhocctlGetState"},
{0x8916c003, WrapI_CU<sceNetAdhocctlGetNameByAddr>, "sceNetAdhocctlGetNameByAddr"},
{0xded9d28e, WrapI_U<sceNetAdhocctlGetParameter>, "sceNetAdhocctlGetParameter"},
{0x81aee1be, WrapI_UU<sceNetAdhocctlGetScanInfo>, "sceNetAdhocctlGetScanInfo"},
{0x5e7f79c9, WrapI_U<sceNetAdhocctlJoin>, "sceNetAdhocctlJoin"},
{0x8db83fdc, WrapI_CIU<sceNetAdhocctlGetPeerInfo>, "sceNetAdhocctlGetPeerInfo"},
{0xec0635c1, WrapI_C<sceNetAdhocctlCreate>, "sceNetAdhocctlCreate"},
{0xa5c055ce, WrapI_CIIUII<sceNetAdhocctlCreateEnterGameMode>, "sceNetAdhocctlCreateEnterGameMode"},
{0x1ff89745, WrapI_CCII<sceNetAdhocctlJoinEnterGameMode>, "sceNetAdhocctlJoinEnterGameMode"},
{0xcf8e084d, WrapI_V<sceNetAdhocctlExitGameMode>, "sceNetAdhocctlExitGameMode"},
{0xe162cb14, WrapI_UU<sceNetAdhocctlGetPeerList>, "sceNetAdhocctlGetPeerList"},
{0x362cbe8f, WrapI_U<sceNetAdhocctlGetAdhocId>, "sceNetAdhocctlGetAdhocId"},
{0x5a014ce0, WrapI_U<sceNetAdhocctlGetGameModeInfo>, "sceNetAdhocctlGetGameModeInfo"},
{0x99560abe, WrapI_CUU<sceNetAdhocctlGetAddrByName>, "sceNetAdhocctlGetAddrByName"},
{0xb0b80e80, WrapI_CIIIUUI<sceNetAdhocctlCreateEnterGameModeMin>, "sceNetAdhocctlCreateEnterGameModeMin" }, // ??
};
int sceNetAdhocDiscoverInitStart() {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocDiscoverInitStart()");
return 0;
}
int sceNetAdhocDiscoverStop() {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocDiscoverStop()");
return 0;
}
int sceNetAdhocDiscoverTerm() {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocDiscoverTerm()");
return 0;
}
int sceNetAdhocDiscoverUpdate() {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocDiscoverUpdate()");
return 0;
}
int sceNetAdhocDiscoverGetStatus() {
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocDiscoverGetStatus()");
return 0;
}
int sceNetAdhocDiscoverRequestSuspend(void)
{
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocDiscoverRequestSuspend()");
return 0;
}
const HLEFunction sceNetAdhocDiscover[] = {
{0x941B3877, WrapI_V<sceNetAdhocDiscoverInitStart>, "sceNetAdhocDiscoverInitStart" },
{0x52DE1B97, WrapI_V<sceNetAdhocDiscoverUpdate>, "sceNetAdhocDiscoverUpdate" },
{0x944DDBC6, WrapI_V<sceNetAdhocDiscoverGetStatus>, "sceNetAdhocDiscoverGetStatus" },
{0xA2246614, WrapI_V<sceNetAdhocDiscoverTerm>, "sceNetAdhocDiscoverTerm" },
{0xF7D13214, WrapI_V<sceNetAdhocDiscoverStop>, "sceNetAdhocDiscoverStop" },
{0xA423A21B, WrapI_V<sceNetAdhocDiscoverRequestSuspend>, "sceNetAdhocDiscoverRequestSuspend" },
};
void Register_sceNetAdhoc() {
RegisterModule("sceNetAdhoc", ARRAY_SIZE(sceNetAdhoc), sceNetAdhoc);
RegisterModule("sceNetAdhocMatching", ARRAY_SIZE(sceNetAdhocMatching), sceNetAdhocMatching);
RegisterModule("sceNetAdhocDiscover", ARRAY_SIZE(sceNetAdhocDiscover), sceNetAdhocDiscover);
RegisterModule("sceNetAdhocctl", ARRAY_SIZE(sceNetAdhocctl), sceNetAdhocctl);
}
/**
* Broadcast Ping Message to other Matching Users
* @param context Matching Context Pointer
*/
void broadcastPingMessage(SceNetAdhocMatchingContext * context)
{
// Ping Opcode
uint8_t ping = PSP_ADHOC_MATCHING_PACKET_PING;
// Send Broadcast
context->socketlock->lock();
sceNetAdhocPdpSend(context->socket, (const char*)(SceNetEtherAddr *)broadcastMAC, context->port, &ping, sizeof(ping), 0, ADHOC_F_NONBLOCK);
context->socketlock->unlock();
}
/**
* Broadcast Hello Message to other Matching Users
* @param context Matching Context Pointer
*/
void broadcastHelloMessage(SceNetAdhocMatchingContext * context)
{
static uint8_t * hello = NULL;
static int32_t len = -5;
// Allocate Hello Message Buffer, reuse when necessary
if ((int32_t)context->hellolen > len) {
hello = (uint8_t *)realloc(hello, 5 + context->hellolen);
len = context->hellolen;
}
// Allocated Hello Message Buffer
if (hello != NULL)
{
// Hello Opcode
hello[0] = PSP_ADHOC_MATCHING_PACKET_HELLO;
// Hello Data Length (have to memcpy this to avoid cpu alignment crash)
memcpy(hello + 1, &context->hellolen, sizeof(context->hellolen));
// Copy Hello Data
if (context->hellolen > 0) memcpy(hello + 5, context->hello, context->hellolen);
// Send Broadcast
context->socketlock->lock();
sceNetAdhocPdpSend(context->socket, (const char*)(SceNetEtherAddr *)broadcastMAC, context->port, hello, 5 + context->hellolen, 0, ADHOC_F_NONBLOCK);
context->socketlock->unlock();
// Free Memory, not needed since it may be reused again later
//free(hello);
}
}
/**
* Send Accept Packet to Player
* @param context Matching Context Pointer
* @param mac Target Player MAC
* @param optlen Optional Data Length
* @param opt Optional Data
*/
void sendAcceptPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac, int optlen, void * opt)
{
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, mac);
// Found Peer
if (peer != NULL && (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD || peer->state == PSP_ADHOC_MATCHING_PEER_P2P))
{
// Required Sibling Buffer
uint32_t siblingbuflen = 0;
// Parent Mode
if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT) siblingbuflen = sizeof(SceNetEtherAddr) * (countConnectedPeers(context) - 2);
// Sibling Count
int siblingcount = siblingbuflen / sizeof(SceNetEtherAddr);
// Allocate Accept Message Buffer
uint8_t * accept = (uint8_t *)malloc(9 + optlen + siblingbuflen);
// Allocated Accept Message Buffer
if (accept != NULL)
{
// Accept Opcode
accept[0] = PSP_ADHOC_MATCHING_PACKET_ACCEPT;
// Optional Data Length
memcpy(accept + 1, &optlen, sizeof(optlen));
// Sibling Count
memcpy(accept + 5, &siblingcount, sizeof(siblingcount));
// Copy Optional Data
if (optlen > 0) memcpy(accept + 9, opt, optlen);
// Parent Mode Extra Data required
if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && siblingcount > 0)
{
// Create MAC Array Pointer
uint8_t * siblingmacs = (uint8_t *)(accept + 9 + optlen);
// MAC Writing Pointer
int i = 0;
// Iterate Peer List
SceNetAdhocMatchingMemberInternal * item = context->peerlist; for (; item != NULL; item = item->next)
{
// Ignore Target
if (item == peer) continue;
// Copy Child MAC
if (item->state == PSP_ADHOC_MATCHING_PEER_CHILD)
{
// Clone MAC the stupid memcpy way to shut up PSP CPU
memcpy(siblingmacs + sizeof(SceNetEtherAddr) * i++, &item->mac, sizeof(SceNetEtherAddr));
}
}
}
// Send Data
context->socketlock->lock();
sceNetAdhocPdpSend(context->socket, (const char*)mac, context->port, accept, 9 + optlen + siblingbuflen, 0, ADHOC_F_NONBLOCK);
context->socketlock->unlock();
// Free Memory
free(accept);
// Spawn Local Established Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_ESTABLISHED, mac, 0, NULL);
}
}
}
/**
* Send Join Packet to Player
* @param context Matching Context Pointer
* @param mac Target Player MAC
* @param optlen Optional Data Length
* @param opt Optional Data
*/
void sendJoinPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac, int optlen, void * opt)
{
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, mac);
// Valid Peer
if (peer != NULL && peer->state == PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST)
{
// Allocate Join Message Buffer
uint8_t * join = (uint8_t *)malloc(5 + optlen);
// Allocated Join Message Buffer
if (join != NULL)
{
// Join Opcode
join[0] = PSP_ADHOC_MATCHING_PACKET_JOIN;
// Optional Data Length
memcpy(join + 1, &optlen, sizeof(optlen));
// Copy Optional Data
if (optlen > 0) memcpy(join + 5, opt, optlen);
// Send Data
context->socketlock->lock();
sceNetAdhocPdpSend(context->socket, (const char*)mac, context->port, join, 5 + optlen, 0, ADHOC_F_NONBLOCK);
context->socketlock->unlock();
// Free Memory
free(join);
}
}
}
/**
* Send Cancel Packet to Player
* @param context Matching Context Pointer
* @param mac Target Player MAC
* @param optlen Optional Data Length
* @param opt Optional Data
*/
void sendCancelPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac, int optlen, void * opt)
{
// Allocate Cancel Message Buffer
uint8_t * cancel = (uint8_t *)malloc(5 + optlen);
// Allocated Cancel Message Buffer
if (cancel != NULL)
{
// Cancel Opcode
cancel[0] = PSP_ADHOC_MATCHING_PACKET_CANCEL;
// Optional Data Length
memcpy(cancel + 1, &optlen, sizeof(optlen));
// Copy Optional Data
if (optlen > 0) memcpy(cancel + 5, opt, optlen);
// Send Data
context->socketlock->lock();
sceNetAdhocPdpSend(context->socket, (const char*)mac, context->port, cancel, 5 + optlen, 0, ADHOC_F_NONBLOCK);
context->socketlock->unlock();
// Free Memory
free(cancel);
}
peerlock.lock();
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, mac);
// Found Peer
if (peer != NULL)
{
// Child Mode Fallback - Delete All
if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD)
{
// Delete Peer List
clearPeerList(context);
}
// Delete Peer
else deletePeer(context, peer);
}
peerlock.unlock();
}
/**
* Send Bulk Data Packet to Player
* @param context Matching Context Pointer
* @param mac Target Player MAC
* @param datalen Data Length
* @param data Data
*/
void sendBulkDataPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac, int datalen, void * data)
{
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, mac);
// Valid Peer (rest is already checked in send.c)
if (peer != NULL)
{
// Don't send if it's aborted
//if (peer->sending == 0) return;
// Allocate Send Message Buffer
uint8_t * send = (uint8_t *)malloc(5 + datalen);
// Allocated Send Message Buffer
if (send != NULL)
{
// Send Opcode
send[0] = PSP_ADHOC_MATCHING_PACKET_BULK;
// Data Length
memcpy(send + 1, &datalen, sizeof(datalen));
// Copy Data
memcpy(send + 5, data, datalen);
// Send Data
context->socketlock->lock();
sceNetAdhocPdpSend(context->socket, (const char*)mac, context->port, send, 5 + datalen, 0, ADHOC_F_NONBLOCK);
context->socketlock->unlock();
// Free Memory
free(send);
// Remove Busy Bit from Peer
peer->sending = 0;
// Spawn Data Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_DATA_ACK, mac, 0, NULL);
}
}
}
/**
* Tell Established Peers of new Child
* @param context Matching Context Pointer
* @param mac New Child's MAC
*/
void sendBirthPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac)
{
// Find Newborn Child
SceNetAdhocMatchingMemberInternal * newborn = findPeer(context, mac);
// Found Newborn Child
if (newborn != NULL)
{
// Packet Buffer
uint8_t packet[7];
// Set Opcode
packet[0] = PSP_ADHOC_MATCHING_PACKET_BIRTH;
// Set Newborn MAC
memcpy(packet + 1, mac, sizeof(SceNetEtherAddr));
// Iterate Peers
SceNetAdhocMatchingMemberInternal * peer = context->peerlist; for (; peer != NULL; peer = peer->next)
{
// Skip Newborn Child
if (peer == newborn) continue;
// Send only to children
if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD)
{
// Send Packet
context->socketlock->lock();
int sent = sceNetAdhocPdpSend(context->socket, (const char*)&peer->mac, context->port, packet, sizeof(packet), 0, ADHOC_F_NONBLOCK);
context->socketlock->unlock();
// Log Send Success
if (sent >= 0)
INFO_LOG(SCENET, "InputLoop: Sending BIRTH to %02X:%02X:%02X:%02X:%02X:%02X", peer->mac.data[0], peer->mac.data[1], peer->mac.data[2], peer->mac.data[3], peer->mac.data[4], peer->mac.data[5]);
else
WARN_LOG(SCENET, "InputLoop: Failed to Send BIRTH to %02X:%02X:%02X:%02X:%02X:%02X", peer->mac.data[0], peer->mac.data[1], peer->mac.data[2], peer->mac.data[3], peer->mac.data[4], peer->mac.data[5]);
}
}
}
}
/**
* Tell Established Peers of abandoned Child
* @param context Matching Context Pointer
* @param mac Dead Child's MAC
*/
void sendDeathPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac)
{
// Find abandoned Child
SceNetAdhocMatchingMemberInternal * deadkid = findPeer(context, mac);
// Found abandoned Child
if (deadkid != NULL)
{
// Packet Buffer
uint8_t packet[7];
// Set Opcode
packet[0] = PSP_ADHOC_MATCHING_PACKET_DEATH;
// Set abandoned Child MAC
memcpy(packet + 1, mac, sizeof(SceNetEtherAddr));
// Iterate Peers
SceNetAdhocMatchingMemberInternal * peer = context->peerlist; for (; peer != NULL; peer = peer->next)
{
// Skip dead Child
if (peer == deadkid) continue;
// Send only to children
if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD)
{
// Send Packet
context->socketlock->lock();
sceNetAdhocPdpSend(context->socket, (const char*)&peer->mac, context->port, packet, sizeof(packet), 0, ADHOC_F_NONBLOCK);
context->socketlock->unlock();
}
}
}
}
/**
* Tell Established Peers that we're shutting the Networking Layer down
* @param context Matching Context Pointer
*/
void sendByePacket(SceNetAdhocMatchingContext * context)
{
// Iterate Peers
SceNetAdhocMatchingMemberInternal * peer = context->peerlist; for (; peer != NULL; peer = peer->next)
{
// Peer of Interest
if (peer->state == PSP_ADHOC_MATCHING_PEER_PARENT || peer->state == PSP_ADHOC_MATCHING_PEER_CHILD || peer->state == PSP_ADHOC_MATCHING_PEER_P2P)
{
// Bye Opcode
uint8_t opcode = PSP_ADHOC_MATCHING_PACKET_BYE;
// Send Bye Packet
context->socketlock->lock();
sceNetAdhocPdpSend(context->socket, (const char*)&peer->mac, context->port, &opcode, sizeof(opcode), 0, ADHOC_F_NONBLOCK);
context->socketlock->unlock();
}
}
}
/**
* Handle Ping Packet
* @param context Matching Context Pointer
* @param sendermac Packet Sender MAC
*/
void actOnPingPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac)
{
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
// Found Peer
if (peer != NULL)
{
// Update Receive Timer
peer->lastping = CoreTiming::GetGlobalTimeUsScaled(); //real_time_now()*1000000.0;
}
}
/**
* Handle Hello Packet
* @param context Matching Context Pointer
* @param sendermac Packet Sender MAC
* @param length Packet Length
*/
void actOnHelloPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, int32_t length)
{
// Interested in Hello Data
if ((context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && findParent(context) == NULL) || (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && findP2P(context) == NULL))
{
// Complete Packet Header available
if (length >= 5)
{
// Extract Optional Data Length
int optlen = 0; memcpy(&optlen, context->rxbuf + 1, sizeof(optlen));
// Complete Valid Packet available
if (optlen >= 0 && length >= (5 + optlen))
{
// Set Default Null Data
void * opt = NULL;
// Extract Optional Data Pointer
if (optlen > 0) opt = context->rxbuf + 5;
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
// Peer not found
if (peer == NULL)
{
// Allocate Memory
peer = (SceNetAdhocMatchingMemberInternal *)malloc(sizeof(SceNetAdhocMatchingMemberInternal));
// Allocated Memory
if (peer != NULL)
{
// Clear Memory
memset(peer, 0, sizeof(SceNetAdhocMatchingMemberInternal));
// Copy Sender MAC
peer->mac = *sendermac;
// Set Peer State
peer->state = PSP_ADHOC_MATCHING_PEER_OFFER;
// Initialize Ping Timer
peer->lastping = CoreTiming::GetGlobalTimeUsScaled(); //real_time_now()*1000000.0;
// Link Peer into List
peer->next = context->peerlist;
context->peerlist = peer;
}
}
// Peer available now
if (peer != NULL)
{
// Spawn Hello Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_HELLO, sendermac, optlen, opt);
}
}
}
}
}
/**
* Handle Join Packet
* @param context Matching Context Pointer
* @param sendermac Packet Sender MAC
* @param length Packet Length
*/
void actOnJoinPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, int32_t length)
{
// Not a child mode context
if (context->mode != PSP_ADHOC_MATCHING_MODE_CHILD)
{
// We still got a unoccupied slot in our room (Parent / P2P)
if ((context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && countChildren(context) < (context->maxpeers - 1)) || (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && findP2P(context) == NULL))
{
// Complete Packet Header available
if (length >= 5)
{
// Extract Optional Data Length
int optlen = 0; memcpy(&optlen, context->rxbuf + 1, sizeof(optlen));
// Complete Valid Packet available
if (optlen >= 0 && length >= (5 + optlen))
{
// Set Default Null Data
void * opt = NULL;
// Extract Optional Data Pointer
if (optlen > 0) opt = context->rxbuf + 5;
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
// If we got the peer in the table already and are a parent, there is nothing left to be done.
// This is because the only way a parent can know of a child is via a join request...
// If we thus know of a possible child, then we already had a previous join request thus no need for double tapping.
if (peer != NULL && context->mode == PSP_ADHOC_MATCHING_MODE_PARENT) return;
// New Peer
if (peer == NULL)
{
// Allocate Memory
peer = (SceNetAdhocMatchingMemberInternal *)malloc(sizeof(SceNetAdhocMatchingMemberInternal));
// Allocated Memory
if (peer != NULL)
{
// Clear Memory
memset(peer, 0, sizeof(SceNetAdhocMatchingMemberInternal));
// Copy Sender MAC
peer->mac = *sendermac;
// Set Peer State
peer->state = PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST;
// Initialize Ping Timer
peer->lastping = CoreTiming::GetGlobalTimeUsScaled(); //real_time_now()*1000000.0;
// Link Peer into List
peer->next = context->peerlist;
context->peerlist = peer;
// Spawn Request Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_REQUEST, sendermac, optlen, opt);
// Return Success
return;
}
}
// Existing Peer (this case is only reachable for P2P mode)
else
{
// Set Peer State
peer->state = PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST;
// Spawn Request Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_REQUEST, sendermac, optlen, opt);
// Return Success
return;
}
}
}
}
INFO_LOG(SCENET, "Join Event(2) Rejected");
// Auto-Reject Player
sendCancelPacket(context, sendermac, 0, NULL);
}
}
/**
* Handle Accept Packet
* @param context Matching Context Pointer
* @param sendermac Packet Sender MAC
* @param length Packet Length
*/
void actOnAcceptPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, uint32_t length)
{
// Not a parent context
if (context->mode != PSP_ADHOC_MATCHING_MODE_PARENT)
{
// Don't have a master yet
if ((context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && findParent(context) == NULL) || (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && findP2P(context) == NULL))
{
// Complete Packet Header available
if (length >= 9)
{
// Extract Optional Data Length
int optlen = 0; memcpy(&optlen, context->rxbuf + 1, sizeof(optlen));
// Extract Sibling Count
int siblingcount = 0; memcpy(&siblingcount, context->rxbuf + 5, sizeof(siblingcount));
// Complete Valid Packet available
if (optlen >= 0 && length >= (9 + optlen + sizeof(SceNetEtherAddr) * siblingcount))
{
// Set Default Null Data
void * opt = NULL;
// Extract Optional Data Pointer
if (optlen > 0) opt = context->rxbuf + 9;
// Sibling MAC Array Null Data
SceNetEtherAddr * siblings = NULL;
// Extract Optional Sibling MAC Array
if (siblingcount > 0) siblings = (SceNetEtherAddr *)(context->rxbuf + 9 + optlen);
// Find Outgoing Request
SceNetAdhocMatchingMemberInternal * request = findOutgoingRequest(context);
// We are waiting for a answer to our request...
if (request != NULL)
{
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
// It's the answer we wanted!
if (request == peer)
{
// Change Peer State
peer->state = (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD) ? (PSP_ADHOC_MATCHING_PEER_PARENT) : (PSP_ADHOC_MATCHING_PEER_P2P);
// Remove Unneeded Peer Information
postAcceptCleanPeerList(context);
// Add Sibling Peers
if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD) postAcceptAddSiblings(context, siblingcount, siblings);
// IMPORTANT! The Event Order here is ok!
// Internally the Event Stack appends to the front, so the order will be switched around.
// Spawn Established Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_ESTABLISHED, sendermac, 0, NULL);
// Spawn Accept Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_ACCEPT, sendermac, optlen, opt);
}
}
}
}
}
}
}
/**
* Handle Cancel Packet
* @param context Matching Context Pointer
* @param sendermac Packet Sender MAC
* @param length Packet Length
*/
void actOnCancelPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, int32_t length)
{
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
// Get Parent
SceNetAdhocMatchingMemberInternal * parent = findParent(context);
// Get Outgoing Join Request
SceNetAdhocMatchingMemberInternal * request = findOutgoingRequest(context);
// Get P2P Partner
SceNetAdhocMatchingMemberInternal * p2p = findP2P(context);
// Interest Condition fulfilled
if (peer != NULL)
{
// Complete Packet Header available
if (length >= 5)
{
// Extract Optional Data Length
int optlen = 0; memcpy(&optlen, context->rxbuf + 1, sizeof(optlen));
// Complete Valid Packet available
if (optlen >= 0 && length >= (5 + optlen))
{
// Set Default Null Data
void * opt = NULL;
// Extract Optional Data Pointer
if (optlen > 0) opt = context->rxbuf + 5;
// Child Mode
if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD)
{
// Join Request denied
if (request == peer)
{
// Spawn Deny Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_DENY, sendermac, optlen, opt);
// Delete Peer from List
deletePeer(context, peer);
}
// Kicked from Room
else if (parent == peer)
{
// Iterate Peers
SceNetAdhocMatchingMemberInternal * item = context->peerlist; for (; item != NULL; item = item->next)
{
// Established Peer
if (item->state == PSP_ADHOC_MATCHING_PEER_CHILD || item->state == PSP_ADHOC_MATCHING_PEER_PARENT)
{
// Spawn Leave / Kick Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_LEAVE, &item->mac, optlen, opt);
}
}
// Delete Peer from List
clearPeerList(context);
}
}
// Parent Mode
else if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT)
{
// Cancel Join Request
if (peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST)
{
// Spawn Request Cancel Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_CANCEL, sendermac, optlen, opt);
// Delete Peer from List
deletePeer(context, peer);
}
// Leave Room
else if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD)
{
// Spawn Leave / Kick Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_LEAVE, sendermac, optlen, opt);
// Delete Peer from List
deletePeer(context, peer);
}
}
// P2P Mode
else
{
// Join Request denied
if (request == peer)
{
// Spawn Deny Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_DENY, sendermac, optlen, opt);
// Delete Peer from List
deletePeer(context, peer);
}
// Kicked from Room
else if (p2p == peer)
{
// Spawn Leave / Kick Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_LEAVE, sendermac, optlen, opt);
// Delete Peer from List
deletePeer(context, peer);
}
// Cancel Join Request
else if (peer->state == PSP_ADHOC_MATCHING_PEER_INCOMING_REQUEST)
{
// Spawn Request Cancel Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_CANCEL, sendermac, optlen, opt);
// Delete Peer from List
deletePeer(context, peer);
}
}
}
}
}
}
/**
* Handle Bulk Data Packet
* @param context Matching Context Pointer
* @param sendermac Packet Sender MAC
* @param length Packet Length
*/
void actOnBulkDataPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, int32_t length)
{
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
// Established Peer
if (peer != NULL && (
(context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) ||
(context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD || peer->state == PSP_ADHOC_MATCHING_PEER_PARENT)) ||
(context->mode == PSP_ADHOC_MATCHING_MODE_P2P && peer->state == PSP_ADHOC_MATCHING_PEER_P2P)))
{
// Complete Packet Header available
if (length > 5)
{
// Extract Data Length
int datalen = 0; memcpy(&datalen, context->rxbuf + 1, sizeof(datalen));
// Complete Valid Packet available
if (datalen > 0 && length >= (5 + datalen))
{
// Extract Data
void * data = context->rxbuf + 5;
// Spawn Data Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_DATA, sendermac, datalen, data);
}
}
}
}
/**
* Handle Birth Packet
* @param context Matching Context Pointer
* @param sendermac Packet Sender MAC
* @param length Packet Length
*/
void actOnBirthPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, uint32_t length)
{
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
// Valid Circumstances
if (peer != NULL && context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && peer == findParent(context))
{
// Complete Packet available
if (length >= (1 + sizeof(SceNetEtherAddr)))
{
// Extract Child MAC
SceNetEtherAddr mac;
memcpy(&mac, context->rxbuf + 1, sizeof(SceNetEtherAddr));
// Allocate Memory (If this fails... we are fucked.)
SceNetAdhocMatchingMemberInternal * sibling = (SceNetAdhocMatchingMemberInternal *)malloc(sizeof(SceNetAdhocMatchingMemberInternal));
// Allocated Memory
if (sibling != NULL)
{
// Clear Memory
memset(sibling, 0, sizeof(SceNetAdhocMatchingMemberInternal));
// Save MAC Address
sibling->mac = mac;
// Set Peer State
sibling->state = PSP_ADHOC_MATCHING_PEER_CHILD;
// Initialize Ping Timer
peer->lastping = CoreTiming::GetGlobalTimeUsScaled(); //real_time_now()*1000000.0;
peerlock.lock();
// Link Peer
sibling->next = context->peerlist;
context->peerlist = sibling;
peerlock.unlock();
// Spawn Established Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_ESTABLISHED, &sibling->mac, 0, NULL);
}
}
}
}
/**
* Handle Death Packet
* @param context Matching Context Pointer
* @param sendermac Packet Sender MAC
* @param length Packet Length
*/
void actOnDeathPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac, uint32_t length)
{
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
// Valid Circumstances
if (peer != NULL && context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && peer == findParent(context))
{
// Complete Packet available
if (length >= (1 + sizeof(SceNetEtherAddr)))
{
// Extract Child MAC
SceNetEtherAddr mac;
memcpy(&mac, context->rxbuf + 1, sizeof(SceNetEtherAddr));
// Find Peer
SceNetAdhocMatchingMemberInternal * deadkid = findPeer(context, &mac);
// Valid Sibling
if (deadkid->state == PSP_ADHOC_MATCHING_PEER_CHILD)
{
// Spawn Leave Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_LEAVE, &mac, 0, NULL);
// Delete Peer
deletePeer(context, deadkid);
}
}
}
}
/**
* Handle Bye Packet
* @param context Matching Context Pointer
* @param sendermac Packet Sender MAC
*/
void actOnByePacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * sendermac)
{
// Find Peer
SceNetAdhocMatchingMemberInternal * peer = findPeer(context, sendermac);
// We know this guy
if (peer != NULL)
{
// P2P or Child Bye
if ((context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) ||
(context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) ||
(context->mode == PSP_ADHOC_MATCHING_MODE_P2P && peer->state == PSP_ADHOC_MATCHING_PEER_P2P))
{
// Spawn Leave / Kick Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_BYE, sendermac, 0, NULL);
// Delete Peer
deletePeer(context, peer);
}
// Parent Bye
else if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && peer->state == PSP_ADHOC_MATCHING_PEER_PARENT)
{
// Iterate Peers
SceNetAdhocMatchingMemberInternal * item = context->peerlist; for (; item != NULL; item = item->next)
{
// Established Peer
if (item->state == PSP_ADHOC_MATCHING_PEER_CHILD || item->state == PSP_ADHOC_MATCHING_PEER_PARENT)
{
// Spawn Leave / Kick Event
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_BYE, &item->mac, 0, NULL);
}
}
// Delete Peer from List
clearPeerList(context);
}
}
}
/**
* Matching Event Dispatcher Thread
* @param args sizeof(SceNetAdhocMatchingContext *)
* @param argp SceNetAdhocMatchingContext *
* @return Exit Point is never reached...
*/
int matchingEventThread(int matchingId)
{
u32 bufLen = 0;
u32 bufAddr = 0;
// Multithreading Lock
peerlock.lock();
// Cast Context
SceNetAdhocMatchingContext * context = findMatchingContext(matchingId);
// Multithreading Unlock
peerlock.unlock();
// Log Startup
INFO_LOG(SCENET, "EventLoop: Begin of EventLoop[%i] Thread", matchingId);
// Run while needed...
if (context != NULL) {
//static u32_le args[5] = { 0, 0, 0, 0, 0 }; // Need to be global/static so it can be accessed from a different thread
u32_le * args = context->handlerArgs;
while (context->eventRunning) // (context->eventThread.get_id() != NULL)
{
// Messages on Stack ready for processing
if (context->event_stack != NULL)
{
// Claim Stack
context->eventlock->lock();
// Iterate Message List
ThreadMessage * msg = context->event_stack; for (; msg != NULL; msg = msg->next)
{
// Default Optional Data
void * opt = NULL;
// Grab Optional Data
if (msg->optlen > 0) opt = ((u8 *)msg) + sizeof(ThreadMessage); //&msg[1]
// Log Matching Events
if (msg->opcode >= 0) {
char buf[16];
INFO_LOG(SCENET, "EventLoop[%d]: Matching Event [%d=%s] OptSize=%d", matchingId, msg->opcode, getMatchingEventStr(msg->opcode, buf), msg->optlen);
}
context->eventlock->unlock(); // Unlock to prevent race-condition with other threads due to recursive lock
// Call Event Handler
//context->handler(context->id, msg->opcode, &msg->mac, msg->optlen, opt);
// Notify Event Handlers
notifyMatchingHandler(context, msg, opt, bufAddr, bufLen, args);
context->eventlock->lock(); // Lock again
}
// Clear Event Message Stack
clearStack(context, PSP_ADHOC_MATCHING_EVENT_STACK);
// Free Stack
context->eventlock->unlock();
}
// Share CPU Time
sleep_ms(1); //10 //sceKernelDelayThread(10000);
// Don't do anything if it's paused, otherwise the log will be flooded
while (Core_IsStepping() && context->eventRunning) sleep_ms(1);
}
// Process Last Messages
if (context->event_stack != NULL)
{
// Claim Stack
context->eventlock->lock();
// Iterate Message List
ThreadMessage * msg = context->event_stack; for (; msg != NULL; msg = msg->next)
{
// Default Optional Data
void * opt = NULL;
// Grab Optional Data
if (msg->optlen > 0) opt = ((u8 *)msg) + sizeof(ThreadMessage); //&msg[1]
INFO_LOG(SCENET, "EventLoop[%d]: Matching Event [EVENT=%d]\n", matchingId, msg->opcode);
context->eventlock->unlock();
// Original Call Event Handler
//context->handler(context->id, msg->opcode, &msg->mac, msg->optlen, opt);
// Notify Event Handlers
notifyMatchingHandler(context, msg, opt, bufAddr, bufLen, args);
context->eventlock->lock();
}
// Clear Event Message Stack
clearStack(context, PSP_ADHOC_MATCHING_EVENT_STACK);
// Free Stack
context->eventlock->unlock();
}
// Delete Pointer Reference (and notify caller about finished cleanup)
//context->eventThread = NULL;
}
// Log Shutdown
INFO_LOG(SCENET, "EventLoop: End of EventLoop[%i] Thread", matchingId);
// Free memory
if (Memory::IsValidAddress(bufAddr)) userMemory.Free(bufAddr);
// Return Zero to shut up Compiler (never reached anyway)
return 0;
}
/**
* Matching IO Handler Thread
* @param args sizeof(SceNetAdhocMatchingContext *)
* @param argp SceNetAdhocMatchingContext *
* @return Exit Point is never reached...
*/
int matchingInputThread(int matchingId)
{
// Multithreading Lock
peerlock.lock();
// Cast Context
SceNetAdhocMatchingContext * context = findMatchingContext(matchingId);
// Multithreading Unlock
peerlock.unlock();
// Last Ping
u64_le lastping = 0;
// Last Hello
u64_le lasthello = 0;
u64_le now;
// Log Startup
INFO_LOG(SCENET, "InputLoop: Begin of InputLoop[%i] Thread", matchingId);
// Run while needed...
if (context != NULL) {
while (context->inputRunning)
{
now = CoreTiming::GetGlobalTimeUsScaled(); //real_time_now()*1000000.0;
// Hello Message Sending Context with unoccupied Slots
if ((context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && (countChildren(context) < (context->maxpeers - 1))) || (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && findP2P(context) == NULL))
{
// Hello Message Broadcast necessary because of Hello Interval
if (context->hello_int > 0)
if ((now - lasthello) >= context->hello_int)
{
// Broadcast Hello Message
broadcastHelloMessage(context);
// Update Hello Timer
lasthello = now;
}
}
// Ping Required
if (context->keepalive_int > 0)
if ((now - lastping) >= context->keepalive_int)
{
// Broadcast Ping Message
broadcastPingMessage(context);
// Update Ping Timer
lastping = now;
}
// Messages on Stack ready for processing
if (context->input_stack != NULL)
{
// Claim Stack
context->inputlock->lock();
// Iterate Message List
ThreadMessage * msg = context->input_stack;
for (; msg != NULL; msg = msg->next)
{
// Default Optional Data
void * opt = NULL;
// Grab Optional Data
if (msg->optlen > 0) opt = ((u8 *)msg) + sizeof(ThreadMessage);
context->inputlock->unlock(); // Unlock to prevent race condition when locking peerlock
// Send Accept Packet
if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_ACCEPT) sendAcceptPacket(context, &msg->mac, msg->optlen, opt);
// Send Join Packet
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_JOIN) sendJoinPacket(context, &msg->mac, msg->optlen, opt);
// Send Cancel Packet
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_CANCEL) sendCancelPacket(context, &msg->mac, msg->optlen, opt);
// Send Bulk Data Packet
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK) sendBulkDataPacket(context, &msg->mac, msg->optlen, opt);
// Send Birth Packet
else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BIRTH) sendBirthPacket(context, &msg->mac);
// Cancel Bulk Data Transfer (does nothing as of now as we fire and forget anyway) // Do we need to check DeathPacket and BytePacket here?
// else if(msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK_ABORT) blabla;
context->inputlock->lock(); // Lock again
}
// Clear IO Message Stack
clearStack(context, PSP_ADHOC_MATCHING_INPUT_STACK);
// Free Stack
context->inputlock->unlock();
}
// Receive PDP Datagram
SceNetEtherAddr sendermac;
uint16_t senderport;
int rxbuflen = context->rxbuflen;
context->socketlock->lock();
int recvresult = sceNetAdhocPdpRecv(context->socket, &sendermac, &senderport, context->rxbuf, &rxbuflen, 0, ADHOC_F_NONBLOCK);
context->socketlock->unlock();
// Received Data from a Sender that interests us
if (recvresult == 0 && rxbuflen > 0 && context->port == senderport)
{
// Log Receive Success
if (context->rxbuf[0] > 1) {
char buf[16];
INFO_LOG(SCENET, "InputLoop[%d]: Received %d Bytes (Opcode[%d]=%s)", matchingId, rxbuflen, context->rxbuf[0], getMatchingOpcodeStr(context->rxbuf[0], buf));
}
// Update Peer Timestamp
peerlock.lock();
SceNetAdhocctlPeerInfo * peer = findFriend(&sendermac);
if (peer != NULL) {
now = CoreTiming::GetGlobalTimeUsScaled();
u64_le delta = now - peer->last_recv;
DEBUG_LOG(SCENET, "Timestamp Delta: %llu (%llu - %llu)", delta, now, peer->last_recv);
if (/*context->rxbuf[0] > 0 &&*/ peer->last_recv != 0) peer->last_recv = now; // - context->keepalive_int; // May need to deduce by ping interval to prevent Dissidia 012 unable to see other players (ie. disappearing issue)
}
peerlock.unlock();
// Ping Packet
if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_PING) actOnPingPacket(context, &sendermac);
// Hello Packet
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_HELLO) actOnHelloPacket(context, &sendermac, rxbuflen);
// Join Packet
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_JOIN) actOnJoinPacket(context, &sendermac, rxbuflen);
// Accept Packet
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_ACCEPT) actOnAcceptPacket(context, &sendermac, rxbuflen);
// Cancel Packet
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_CANCEL) actOnCancelPacket(context, &sendermac, rxbuflen);
// Bulk Data Packet
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BULK) actOnBulkDataPacket(context, &sendermac, rxbuflen);
// Birth Packet
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BIRTH) actOnBirthPacket(context, &sendermac, rxbuflen);
// Death Packet
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_DEATH) actOnDeathPacket(context, &sendermac, rxbuflen);
// Bye Packet
else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BYE) actOnByePacket(context, &sendermac);
// Ignore Incoming Trash Data
}
// Handle Peer Timeouts
handleTimeout(context);
// Share CPU Time
sleep_ms(1); //10 //sceKernelDelayThread(10000);
// Don't do anything if it's paused, otherwise the log will be flooded
while (Core_IsStepping() && context->inputRunning) sleep_ms(1);
}
// Send Bye Messages
sendByePacket(context);
// Free Peer List Buffer
clearPeerList(context); //deleteAllMembers(context);
// Delete Pointer Reference (and notify caller about finished cleanup)
//context->inputThread = NULL;
}
// Log Shutdown
INFO_LOG(SCENET, "InputLoop: End of InputLoop[%i] Thread", matchingId);
// Terminate Thread
//sceKernelExitDeleteThread(0);
// Return Zero to shut up Compiler (never reached anyway)
return 0;
}