mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-12-25 06:36:05 +00:00
5061 lines
159 KiB
C++
5061 lines
159 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/MemMapHelpers.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"
|
|
|
|
#include "base/mutex.h"
|
|
|
|
// shared in sceNetAdhoc.h since it need to be used from sceNet.cpp also
|
|
// TODO: Make accessor functions instead, and throw all this state in a struct.
|
|
bool netAdhocInited;
|
|
bool netAdhocctlInited;
|
|
bool networkInited;
|
|
|
|
static bool netAdhocMatchingInited;
|
|
int netAdhocMatchingStarted = 0;
|
|
|
|
SceUID threadAdhocID;
|
|
|
|
recursive_mutex adhocEvtMtx;
|
|
std::vector<std::pair<u32, u32>> adhocctlEvents;
|
|
std::vector<u64> matchingEvents;
|
|
u32 dummyThreadHackAddr = 0;
|
|
u32_le dummyThreadCode[3];
|
|
|
|
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);
|
|
|
|
void __NetAdhocShutdown() {
|
|
//Kill AdhocServer Thread
|
|
if (adhocServerRunning) {
|
|
adhocServerRunning = false;
|
|
if (adhocServerThread.joinable()) {
|
|
adhocServerThread.join();
|
|
}
|
|
}
|
|
// Checks to avoid confusing logspam
|
|
if (netAdhocMatchingInited) {
|
|
sceNetAdhocMatchingTerm();
|
|
}
|
|
if (netAdhocctlInited) {
|
|
sceNetAdhocctlTerm();
|
|
}
|
|
if (netAdhocInited) {
|
|
// Should not really call HLE funcs from shutdown.
|
|
// Prevent attempting to delete the thread, already deleted by shutdown.
|
|
threadAdhocID = 0;
|
|
sceNetAdhocTerm();
|
|
}
|
|
if (dummyThreadHackAddr) {
|
|
kernelMemory.Free(dummyThreadHackAddr);
|
|
dummyThreadHackAddr = 0;
|
|
}
|
|
}
|
|
|
|
void __NetAdhocDoState(PointerWrap &p) {
|
|
auto s = p.Section("sceNetAdhoc", 1, 2);
|
|
if (!s)
|
|
return;
|
|
|
|
p.Do(netAdhocInited);
|
|
p.Do(netAdhocctlInited);
|
|
p.Do(netAdhocMatchingInited);
|
|
p.Do(adhocctlHandlers);
|
|
|
|
if (s >= 2) {
|
|
p.Do(actionAfterMatchingMipsCall);
|
|
__KernelRestoreActionType(actionAfterMatchingMipsCall, AfterMatchingMipsCall::Create);
|
|
|
|
p.Do(dummyThreadHackAddr);
|
|
} else if (p.mode == p.MODE_READ) {
|
|
// Previously, this wasn't being saved. It needs its own space.
|
|
if (strcmp("dummythreadhack", kernelMemory.GetBlockTag(dummyThreadHackAddr)) != 0) {
|
|
u32 blockSize = sizeof(dummyThreadCode);
|
|
dummyThreadHackAddr = kernelMemory.Alloc(blockSize, false, "dummythreadhack");
|
|
}
|
|
}
|
|
if (dummyThreadHackAddr) {
|
|
Memory::Memcpy(dummyThreadHackAddr, dummyThreadCode, sizeof(dummyThreadCode));
|
|
}
|
|
}
|
|
|
|
void __UpdateAdhocctlHandlers(u32 flag, u32 error) {
|
|
lock_guard adhocGuard(adhocEvtMtx);
|
|
adhocctlEvents.push_back({ flag, error });
|
|
}
|
|
|
|
// TODO: MipsCall needs to be called from it's own PSP Thread instead of from any random PSP Thread
|
|
void __UpdateMatchingHandler(u64 ArgsPtr) {
|
|
lock_guard adhocGuard(adhocEvtMtx);
|
|
matchingEvents.push_back(ArgsPtr);
|
|
}
|
|
|
|
static int getBlockingFlag(int id) {
|
|
#ifdef _MSC_VER
|
|
return 0;
|
|
#else
|
|
int sockflag = fcntl(id, F_GETFL, O_NONBLOCK);
|
|
return sockflag & O_NONBLOCK;
|
|
#endif
|
|
}
|
|
|
|
void __NetAdhocInit() {
|
|
friendFinderRunning = false;
|
|
netAdhocInited = false;
|
|
netAdhocctlInited = false;
|
|
netAdhocMatchingInited = false;
|
|
adhocctlHandlers.clear();
|
|
__AdhocServerInit();
|
|
dummyThreadCode[0] = MIPS_MAKE_SYSCALL("sceNetAdhoc", "__NetTriggerCallbacks");
|
|
dummyThreadCode[1] = MIPS_MAKE_B(-2);
|
|
dummyThreadCode[2] = MIPS_MAKE_NOP();
|
|
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);
|
|
// Create built-in AdhocServer Thread
|
|
if (g_Config.bEnableWlan && g_Config.bEnableAdhocServer) {
|
|
adhocServerRunning = true;
|
|
adhocServerThread = std::thread(proAdhocServerThread, SERVER_PORT);
|
|
}
|
|
}
|
|
|
|
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);
|
|
threadAdhocID = __KernelCreateThread("AdhocThread", __KernelGetCurThreadModuleId(), dummyThreadHackAddr, 0x10, 0x1000, 0, PSP_THREAD_ATTR_KERNEL);
|
|
if (threadAdhocID > 0) {
|
|
__KernelStartThread(threadAdhocID, 0, 0);
|
|
}
|
|
|
|
// 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 (netAdhocctlInited)
|
|
return ERROR_NET_ADHOCCTL_ALREADY_INITIALIZED;
|
|
|
|
if(g_Config.bEnableWlan) {
|
|
if (initNetwork((SceNetAdhocctlAdhocId *)Memory::GetPointer(productAddr)) == 0) {
|
|
if (!friendFinderRunning) {
|
|
friendFinderRunning = true;
|
|
friendFinderThread = std::thread(friendFinder);
|
|
}
|
|
networkInited = true;
|
|
} else {
|
|
WARN_LOG(SCENET, "sceNetAdhocctlInit: Failed to init the network but faking success");
|
|
networkInited = false; // TODO: What needs to check this? Pretty much everything? Maybe we should just set netAdhocctlInited to false..
|
|
}
|
|
}
|
|
|
|
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 + portOffset ); // 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) - portOffset; //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,¶meter);
|
|
// 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_t dport = (uint16_t)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 + portOffset);
|
|
|
|
// 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 + portOffset);
|
|
|
|
// 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) - portOffset;
|
|
|
|
// 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 > (int)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 (auto 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(¶meter.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)
|
|
{
|
|
lock_guard adhocGuard(adhocEvtMtx);
|
|
adhocctlEvents.push_back({ 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 != 0)
|
|
{
|
|
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 < (int)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(¶meter.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();
|
|
|
|
// 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 + portOffset);
|
|
|
|
// Bound Socket to local Port
|
|
if (bind(tcpsocket, (sockaddr *)&addr, sizeof(addr)) == 0) {
|
|
// Update sport with the port assigned binternal->lport = ntohs(local.sin_port)y bind
|
|
socklen_t len = sizeof(addr);
|
|
if (getsockname(tcpsocket, (sockaddr *)&addr, &len) == 0) {
|
|
sport = ntohs(addr.sin_port) - portOffset;
|
|
}
|
|
|
|
// 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) - portOffset;
|
|
|
|
// Copy Peer Address Data to Structure
|
|
internal->paddr = mac;
|
|
internal->pport = ntohs(peeraddr.sin_port) - portOffset;
|
|
|
|
// 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 + portOffset);
|
|
|
|
// 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 + portOffset);
|
|
|
|
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 == 0) || (optLen > 0 && optDataPtr != 0))
|
|
{
|
|
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, 0);
|
|
}
|
|
|
|
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 = PSPPointer<s32_le>::Create(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 == 0) || (optLenAddr > 0 && optDataAddr != 0))
|
|
{
|
|
// Grab Existing Hello Data
|
|
void * hello = context->hello;
|
|
|
|
// Free Previous Hello Data, or Reuse it
|
|
//free(hello);
|
|
|
|
// Allocation Required
|
|
if (optLenAddr > 0)
|
|
{
|
|
// Allocate Memory
|
|
if (optLenAddr > context->hellolen) {
|
|
hello = realloc(hello, optLenAddr);
|
|
}
|
|
|
|
// Out of Memory
|
|
if (hello == NULL) {
|
|
context->hellolen = 0;
|
|
return ERROR_NET_ADHOC_MATCHING_NO_SPACE;
|
|
}
|
|
|
|
// Clone Hello Data
|
|
//memcpy(hello, opt, optLenAddr);
|
|
Memory::Memcpy(hello, optDataAddr, optLenAddr);
|
|
|
|
// Set Hello Data
|
|
context->hello = (uint8_t*)hello;
|
|
context->hellolen = optLenAddr;
|
|
context->helloAddr = optDataAddr;
|
|
}
|
|
else
|
|
{
|
|
// Delete Hello Data
|
|
context->hellolen = 0;
|
|
context->helloAddr = 0;
|
|
//free(context->hello); // Doesn't need to free it since it will be reused later
|
|
//context->hello = NULL;
|
|
}
|
|
|
|
// Return Success
|
|
return 0;
|
|
}
|
|
|
|
// Invalid Optional Data Length
|
|
return ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN;
|
|
}
|
|
|
|
// Context not running
|
|
return ERROR_NET_ADHOC_MATCHING_NOT_RUNNING;
|
|
}
|
|
|
|
// 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 != 0)
|
|
{
|
|
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 == 0)
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
void __NetTriggerCallbacks()
|
|
{
|
|
{
|
|
lock_guard adhocGuard(adhocEvtMtx);
|
|
|
|
for (auto ¶ms : adhocctlEvents)
|
|
{
|
|
int flags = params.first;
|
|
int error = params.second;
|
|
u32_le args[3] = { 0, 0, 0 };
|
|
args[0] = flags;
|
|
args[1] = error;
|
|
|
|
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);
|
|
}
|
|
}
|
|
adhocctlEvents.clear();
|
|
|
|
for (auto ¶m : matchingEvents)
|
|
{
|
|
u32_le *args = (u32_le *) param;
|
|
AfterMatchingMipsCall *after = (AfterMatchingMipsCall *) __KernelCreateAction(actionAfterMatchingMipsCall);
|
|
after->SetContextID(args[0], args[1]);
|
|
__KernelDirectMipsCall(args[5], after, args, 5, true);
|
|
}
|
|
matchingEvents.clear();
|
|
}
|
|
//magically make this work
|
|
hleDelayResult(0, "Prevent Adhoc thread from blocking", 1000);
|
|
}
|
|
|
|
const HLEFunction sceNetAdhoc[] = {
|
|
{0XE1D621D7, &WrapU_V<sceNetAdhocInit>, "sceNetAdhocInit", 'x', "" },
|
|
{0XA62C6F57, &WrapI_V<sceNetAdhocTerm>, "sceNetAdhocTerm", 'i', "" },
|
|
{0X0AD043ED, &WrapI_U<sceNetAdhocctlConnect>, "sceNetAdhocctlConnect", 'i', "x" },
|
|
{0X6F92741B, &WrapI_CUIU<sceNetAdhocPdpCreate>, "sceNetAdhocPdpCreate", 'i', "sxix" },
|
|
{0XABED3790, &WrapI_ICUVIII<sceNetAdhocPdpSend>, "sceNetAdhocPdpSend", 'i', "isxpiii" },
|
|
{0XDFE53E03, &WrapI_IVVVVUI<sceNetAdhocPdpRecv>, "sceNetAdhocPdpRecv", 'i', "ippppxi" },
|
|
{0X7F27BB5E, &WrapI_II<sceNetAdhocPdpDelete>, "sceNetAdhocPdpDelete", 'i', "ii" },
|
|
{0XC7C1FC57, &WrapI_UU<sceNetAdhocGetPdpStat>, "sceNetAdhocGetPdpStat", 'i', "xx" },
|
|
{0X157E6225, &WrapI_II<sceNetAdhocPtpClose>, "sceNetAdhocPtpClose", 'i', "ii" },
|
|
{0X4DA4C788, &WrapI_IUUII<sceNetAdhocPtpSend>, "sceNetAdhocPtpSend", 'i', "ixxii" },
|
|
{0X877F6D66, &WrapI_CICIIIII<sceNetAdhocPtpOpen>, "sceNetAdhocPtpOpen", 'i', "sisiiiii" },
|
|
{0X8BEA2B3E, &WrapI_IUUII<sceNetAdhocPtpRecv>, "sceNetAdhocPtpRecv", 'i', "ixxii" },
|
|
{0X9DF81198, &WrapI_IUUII<sceNetAdhocPtpAccept>, "sceNetAdhocPtpAccept", 'i', "ixxii" },
|
|
{0XE08BDAC1, &WrapI_CIIIIII<sceNetAdhocPtpListen>, "sceNetAdhocPtpListen", 'i', "siiiiii" },
|
|
{0XFC6FC07B, &WrapI_III<sceNetAdhocPtpConnect>, "sceNetAdhocPtpConnect", 'i', "iii" },
|
|
{0X9AC2EEAC, &WrapI_III<sceNetAdhocPtpFlush>, "sceNetAdhocPtpFlush", 'i', "iii" },
|
|
{0XB9685118, &WrapI_UU<sceNetAdhocGetPtpStat>, "sceNetAdhocGetPtpStat", 'i', "xx" },
|
|
{0X3278AB0C, &WrapI_CUI<sceNetAdhocGameModeCreateReplica>, "sceNetAdhocGameModeCreateReplica", 'i', "sxi" },
|
|
{0X98C204C8, &WrapI_V<sceNetAdhocGameModeUpdateMaster>, "sceNetAdhocGameModeUpdateMaster", 'i', "" },
|
|
{0XFA324B4E, &WrapI_IU<sceNetAdhocGameModeUpdateReplica>, "sceNetAdhocGameModeUpdateReplica", 'i', "ix" },
|
|
{0XA0229362, &WrapI_V<sceNetAdhocGameModeDeleteMaster>, "sceNetAdhocGameModeDeleteMaster", 'i', "" },
|
|
{0X0B2228E9, &WrapI_I<sceNetAdhocGameModeDeleteReplica>, "sceNetAdhocGameModeDeleteReplica", 'i', "i" },
|
|
{0X7F75C338, &WrapI_UI<sceNetAdhocGameModeCreateMaster>, "sceNetAdhocGameModeCreateMaster", 'i', "xi" },
|
|
{0X73BFD52D, &WrapI_II<sceNetAdhocSetSocketAlert>, "sceNetAdhocSetSocketAlert", 'i', "ii" },
|
|
{0X4D2CE199, &WrapI_IU<sceNetAdhocGetSocketAlert>, "sceNetAdhocGetSocketAlert", 'i', "ix" },
|
|
{0X7A662D6B, &WrapI_UIII<sceNetAdhocPollSocket>, "sceNetAdhocPollSocket", 'i', "xiii" },
|
|
// Fake function for PPSSPP's use.
|
|
{0X756E6E6F, &WrapV_V<__NetTriggerCallbacks>, "__NetTriggerCallbacks", 'v', "" },
|
|
};
|
|
|
|
const HLEFunction sceNetAdhocMatching[] = {
|
|
{0X2A2A1E07, &WrapI_U<sceNetAdhocMatchingInit>, "sceNetAdhocMatchingInit", 'i', "x" },
|
|
{0X7945ECDA, &WrapI_V<sceNetAdhocMatchingTerm>, "sceNetAdhocMatchingTerm", 'i', "" },
|
|
{0XCA5EDA6F, &WrapI_IIIIIIIIU<sceNetAdhocMatchingCreate>, "sceNetAdhocMatchingCreate", 'i', "iiiiiiiix"},
|
|
{0X93EF3843, &WrapI_IIIIIIU<sceNetAdhocMatchingStart>, "sceNetAdhocMatchingStart", 'i', "iiiiiix" },
|
|
{0X32B156B3, &WrapI_I<sceNetAdhocMatchingStop>, "sceNetAdhocMatchingStop", 'i', "i" },
|
|
{0XF16EAF4F, &WrapI_I<sceNetAdhocMatchingDelete>, "sceNetAdhocMatchingDelete", 'i', "i" },
|
|
{0X5E3D4B79, &WrapI_ICIU<sceNetAdhocMatchingSelectTarget>, "sceNetAdhocMatchingSelectTarget", 'i', "isix" },
|
|
{0XEA3C6108, &WrapI_IC<sceNetAdhocMatchingCancelTarget>, "sceNetAdhocMatchingCancelTarget", 'i', "is" },
|
|
{0X8F58BEDF, &WrapI_ICIU<sceNetAdhocMatchingCancelTargetWithOpt>, "sceNetAdhocMatchingCancelTargetWithOpt", 'i', "isix" },
|
|
{0XB5D96C2A, &WrapI_IUU<sceNetAdhocMatchingGetHelloOpt>, "sceNetAdhocMatchingGetHelloOpt", 'i', "ixx" },
|
|
{0XB58E61B7, &WrapI_IIU<sceNetAdhocMatchingSetHelloOpt>, "sceNetAdhocMatchingSetHelloOpt", 'i', "iix" },
|
|
{0XC58BCD9E, &WrapI_IUU<sceNetAdhocMatchingGetMembers>, "sceNetAdhocMatchingGetMembers", 'i', "ixx" },
|
|
{0XF79472D7, &WrapI_ICIU<sceNetAdhocMatchingSendData>, "sceNetAdhocMatchingSendData", 'i', "isix" },
|
|
{0XEC19337D, &WrapI_IC<sceNetAdhocMatchingAbortSendData>, "sceNetAdhocMatchingAbortSendData", 'i', "is" },
|
|
{0X40F8F435, &WrapI_V<sceNetAdhocMatchingGetPoolMaxAlloc>, "sceNetAdhocMatchingGetPoolMaxAlloc", 'i', "" },
|
|
{0X9C5CFB7D, &WrapI_U<sceNetAdhocMatchingGetPoolStat>, "sceNetAdhocMatchingGetPoolStat", 'i', "x" },
|
|
};
|
|
|
|
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", 'x', "iix" },
|
|
{0X9D689E13, &WrapI_V<sceNetAdhocctlTerm>, "sceNetAdhocctlTerm", 'i', "" },
|
|
{0X20B317A0, &WrapU_UU<sceNetAdhocctlAddHandler>, "sceNetAdhocctlAddHandler", 'x', "xx" },
|
|
{0X6402490B, &WrapU_U<sceNetAdhocctlDelHandler>, "sceNetAdhocctlDelHandler", 'x', "x" },
|
|
{0X34401D65, &WrapU_V<sceNetAdhocctlDisconnect>, "sceNetAdhocctlDisconnect", 'x', "" },
|
|
{0X0AD043ED, &WrapI_U<sceNetAdhocctlConnect>, "sceNetAdhocctlConnect", 'i', "x" },
|
|
{0X08FFF7A0, &WrapI_V<sceNetAdhocctlScan>, "sceNetAdhocctlScan", 'i', "" },
|
|
{0X75ECD386, &WrapI_U<sceNetAdhocctlGetState>, "sceNetAdhocctlGetState", 'i', "x" },
|
|
{0X8916C003, &WrapI_CU<sceNetAdhocctlGetNameByAddr>, "sceNetAdhocctlGetNameByAddr", 'i', "sx" },
|
|
{0XDED9D28E, &WrapI_U<sceNetAdhocctlGetParameter>, "sceNetAdhocctlGetParameter", 'i', "x" },
|
|
{0X81AEE1BE, &WrapI_UU<sceNetAdhocctlGetScanInfo>, "sceNetAdhocctlGetScanInfo", 'i', "xx" },
|
|
{0X5E7F79C9, &WrapI_U<sceNetAdhocctlJoin>, "sceNetAdhocctlJoin", 'i', "x" },
|
|
{0X8DB83FDC, &WrapI_CIU<sceNetAdhocctlGetPeerInfo>, "sceNetAdhocctlGetPeerInfo", 'i', "six" },
|
|
{0XEC0635C1, &WrapI_C<sceNetAdhocctlCreate>, "sceNetAdhocctlCreate", 'i', "s" },
|
|
{0XA5C055CE, &WrapI_CIIUII<sceNetAdhocctlCreateEnterGameMode>, "sceNetAdhocctlCreateEnterGameMode", 'i', "siixii" },
|
|
{0X1FF89745, &WrapI_CCII<sceNetAdhocctlJoinEnterGameMode>, "sceNetAdhocctlJoinEnterGameMode", 'i', "ssii" },
|
|
{0XCF8E084D, &WrapI_V<sceNetAdhocctlExitGameMode>, "sceNetAdhocctlExitGameMode", 'i', "" },
|
|
{0XE162CB14, &WrapI_UU<sceNetAdhocctlGetPeerList>, "sceNetAdhocctlGetPeerList", 'i', "xx" },
|
|
{0X362CBE8F, &WrapI_U<sceNetAdhocctlGetAdhocId>, "sceNetAdhocctlGetAdhocId", 'i', "x" },
|
|
{0X5A014CE0, &WrapI_U<sceNetAdhocctlGetGameModeInfo>, "sceNetAdhocctlGetGameModeInfo", 'i', "x" },
|
|
{0X99560ABE, &WrapI_CUU<sceNetAdhocctlGetAddrByName>, "sceNetAdhocctlGetAddrByName", 'i', "sxx" },
|
|
{0XB0B80E80, &WrapI_CIIIUUI<sceNetAdhocctlCreateEnterGameModeMin>, "sceNetAdhocctlCreateEnterGameModeMin", 'i', "siiixxi" }, // ??
|
|
};
|
|
|
|
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", 'i', "" },
|
|
{0X52DE1B97, &WrapI_V<sceNetAdhocDiscoverUpdate>, "sceNetAdhocDiscoverUpdate", 'i', "" },
|
|
{0X944DDBC6, &WrapI_V<sceNetAdhocDiscoverGetStatus>, "sceNetAdhocDiscoverGetStatus", 'i', "" },
|
|
{0XA2246614, &WrapI_V<sceNetAdhocDiscoverTerm>, "sceNetAdhocDiscoverTerm", 'i', "" },
|
|
{0XF7D13214, &WrapI_V<sceNetAdhocDiscoverStop>, "sceNetAdhocDiscoverStop", 'i', "" },
|
|
{0XA423A21B, &WrapI_V<sceNetAdhocDiscoverRequestSuspend>, "sceNetAdhocDiscoverRequestSuspend", 'i', "" },
|
|
};
|
|
|
|
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
|
|
INFO_LOG(SCENET, "EventLoop[%d]: Matching Event [%d=%s] OptSize=%d", matchingId, msg->opcode, getMatchingEventStr(msg->opcode), 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) {
|
|
INFO_LOG(SCENET, "InputLoop[%d]: Received %d Bytes (Opcode[%d]=%s)", matchingId, rxbuflen, context->rxbuf[0], getMatchingOpcodeStr(context->rxbuf[0]));
|
|
}
|
|
|
|
// 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;
|
|
}
|