mirror of
https://github.com/libretro/ppsspp.git
synced 2024-12-04 07:00:51 +00:00
2739 lines
86 KiB
C++
2739 lines
86 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 "Common/ChunkFile.h"
|
|
#include "Core/HLE/FunctionWrappers.h"
|
|
#include "Core/HLE/sceKernelThread.h"
|
|
#include "Core/HLE/sceNetAdhoc.h"
|
|
#include "Core/HLE/proAdhoc.h"
|
|
#include "Core/MemMap.h"
|
|
|
|
enum {
|
|
ERROR_NET_ADHOC_INVALID_SOCKET_ID = 0x80410701,
|
|
ERROR_NET_ADHOC_INVALID_ADDR = 0x80410702,
|
|
ERROR_NET_ADHOC_INVALID_ARG = 0x80410B04,
|
|
ERROR_NET_ADHOC_NO_DATA_AVAILABLE = 0x80410709,
|
|
ERROR_NET_ADHOC_PORT_IN_USE = 0x8041070a,
|
|
ERROR_NET_ADHOC_NOT_INITIALIZED = 0x80410712,
|
|
ERROR_NET_ADHOC_ALREADY_INITIALIZED = 0x80410713,
|
|
ERROR_NET_ADHOC_DISCONNECTED = 0x8041070c,
|
|
ERROR_NET_ADHOC_TIMEOUT = 0x80410715,
|
|
ERROR_NET_ADHOC_NO_ENTRY = 0x80410716,
|
|
ERROR_NET_ADHOC_CONNECTION_REFUSED = 0x80410718,
|
|
ERROR_NET_ADHOC_INVALID_MATCHING_ID = 0x80410807,
|
|
ERROR_NET_ADHOC_MATCHING_ALREADY_INITIALIZED = 0x80410812,
|
|
ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED = 0x80410813,
|
|
ERROR_NET_ADHOC_WOULD_BLOCK = 0x80410709,
|
|
ERROR_NET_ADHOC_INVALID_DATALEN = 0x80410705,
|
|
ERROR_NET_ADHOC_INVALID_PORT = 0x80410703,
|
|
ERROR_NET_ADHOC_NOT_LISTENED = 0x8040070E,
|
|
ERROR_NET_ADHOC_NOT_OPENED = 0x8040070D,
|
|
ERROR_NET_ADHOC_SOCKET_ID_NOT_AVAIL = 0x8041070F,
|
|
ERROR_NET_ADHOC_NOT_CONNECTED = 0x8041070B,
|
|
|
|
ERROR_NET_ADHOCCTL_INVALID_ARG = 0x80410B04,
|
|
ERROR_NET_ADHOCCTL_WLAN_SWITCH_OFF = 0x80410b03,
|
|
ERROR_NET_ADHOCCTL_ALREADY_INITIALIZED = 0x80410b07,
|
|
ERROR_NET_ADHOCCTL_NOT_INITIALIZED = 0x80410b08,
|
|
ERROR_NET_ADHOCCTL_DISCONNECTED = 0x80410b09,
|
|
ERROR_NET_ADHOCCTL_BUSY = 0x80410b10,
|
|
ERROR_NET_ADHOCCTL_TOO_MANY_HANDLERS = 0x80410b12,
|
|
|
|
ERROR_NET_ADHOC_MATCHING_INVALID_MODE = 0x80410801,
|
|
ERROR_NET_ADHOC_MATCHING_INVALID_MAXNUM = 0x80410803,
|
|
ERROR_NET_ADHOC_MATCHING_RXBUF_TOO_SHORT = 0x80410804,
|
|
ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN = 0x80410805,
|
|
ERROR_NET_ADHOC_MATCHING_INVALID_ARG = 0x80410806,
|
|
ERROR_NET_ADHOC_MATCHING_INVALID_ID = 0x80410807,
|
|
ERROR_NET_ADHOC_MATCHING_ID_NOT_AVAIL = 0x80410808,
|
|
ERROR_NET_ADHOC_MATCHING_NO_SPACE = 0x80410809,
|
|
ERROR_NET_ADHOC_MATCHING_IS_RUNNING = 0x8041080A,
|
|
ERROR_NET_ADHOC_MATCHING_NOT_RUNNING = 0x8041080B,
|
|
ERROR_NET_ADHOC_MATCHING_UNKNOWN_TARGET = 0x8041080C,
|
|
ERROR_NET_ADHOC_MATCHING_TARGET_NOT_READY = 0x8041080D,
|
|
ERROR_NET_ADHOC_MATCHING_EXCEED_MAXNUM = 0x8041080E,
|
|
ERROR_NET_ADHOC_MATCHING_REQUEST_IN_PROGRESS = 0x8041080F,
|
|
ERROR_NET_ADHOC_MATCHING_ALREADY_ESTABLISHED = 0x80410810,
|
|
ERROR_NET_ADHOC_MATCHING_BUSY = 0x80410811,
|
|
ERROR_NET_ADHOC_MATCHING_PORT_IN_USE = 0x80410814,
|
|
ERROR_NET_ADHOC_MATCHING_STACKSIZE_TOO_SHORT = 0x80410815,
|
|
ERROR_NET_ADHOC_MATCHING_INVALID_DATALEN = 0x80410816,
|
|
ERROR_NET_ADHOC_MATCHING_NOT_ESTABLISHED = 0x80410817,
|
|
ERROR_NET_ADHOC_MATCHING_DATA_BUSY = 0x80410818,
|
|
|
|
ERROR_NET_NO_SPACE = 0x80410001
|
|
};
|
|
|
|
enum {
|
|
PSP_ADHOC_POLL_READY_TO_SEND = 1,
|
|
PSP_ADHOC_POLL_DATA_AVAILABLE = 2,
|
|
PSP_ADHOC_POLL_CAN_CONNECT = 4,
|
|
PSP_ADHOC_POLL_CAN_ACCEPT = 8,
|
|
};
|
|
|
|
enum {
|
|
/**
|
|
* Matching events used in pspAdhocMatchingCallback
|
|
*/
|
|
/** Hello event. optdata contains data if optlen > 0. */
|
|
PSP_ADHOC_MATCHING_EVENT_HELLO = 1,
|
|
/** Join request. optdata contains data if optlen > 0. */
|
|
PSP_ADHOC_MATCHING_EVENT_JOIN = 2,
|
|
/** Target left matching. */
|
|
PSP_ADHOC_MATCHING_EVENT_LEFT = 3,
|
|
/** Join request rejected. */
|
|
PSP_ADHOC_MATCHING_EVENT_REJECT = 4,
|
|
/** Join request cancelled. */
|
|
PSP_ADHOC_MATCHING_EVENT_CANCEL = 5,
|
|
/** Join request accepted. optdata contains data if optlen > 0. */
|
|
PSP_ADHOC_MATCHING_EVENT_ACCEPT = 6,
|
|
/** Matching is complete. */
|
|
PSP_ADHOC_MATCHING_EVENT_COMPLETE = 7,
|
|
/** Ping timeout event. */
|
|
PSP_ADHOC_MATCHING_EVENT_TIMEOUT = 8,
|
|
/** Error event. */
|
|
PSP_ADHOC_MATCHING_EVENT_ERROR = 9,
|
|
/** Peer disconnect event. */
|
|
PSP_ADHOC_MATCHING_EVENT_DISCONNECT = 10,
|
|
/** Data received event. optdata contains data if optlen > 0. */
|
|
PSP_ADHOC_MATCHING_EVENT_DATA = 11,
|
|
/** Data acknowledged event. */
|
|
PSP_ADHOC_MATCHING_EVENT_DATA_CONFIRM = 12,
|
|
/** Data timeout event. */
|
|
PSP_ADHOC_MATCHING_EVENT_DATA_TIMEOUT = 13,
|
|
|
|
/** Internal ping message. */
|
|
PSP_ADHOC_MATCHING_EVENT_INTERNAL_PING = 100,
|
|
|
|
/**
|
|
* Matching modes used in sceNetAdhocMatchingCreate
|
|
*/
|
|
/** Host */
|
|
PSP_ADHOC_MATCHING_MODE_HOST = 1,
|
|
/** Client */
|
|
PSP_ADHOC_MATCHING_MODE_CLIENT = 2,
|
|
/** Peer to peer */
|
|
PSP_ADHOC_MATCHING_MODE_PTP = 3,
|
|
};
|
|
|
|
const size_t MAX_ADHOCCTL_HANDLERS = 32;
|
|
|
|
// shared in sceNetAdhoc.h since it need to be used from sceNet.cpp also
|
|
/*static*/ bool netAdhocInited;
|
|
/*static*/ bool netAdhocctlInited;
|
|
static bool netAdhocMatchingInited;
|
|
|
|
struct AdhocctlHandler {
|
|
u32 entryPoint;
|
|
u32 argument;
|
|
};
|
|
|
|
static std::map<int, AdhocctlHandler> adhocctlHandlers;
|
|
|
|
void __NetAdhocInit() {
|
|
friendFinderRunning = false;
|
|
eventHandlerUpdate = -1;
|
|
netAdhocInited = false;
|
|
netAdhocctlInited = false;
|
|
netAdhocMatchingInited = false;
|
|
adhocctlHandlers.clear();
|
|
}
|
|
|
|
|
|
int sceNetAdhocTerm();
|
|
int sceNetAdhocctlTerm();
|
|
int sceNetAdhocMatchingTerm();
|
|
int sceNetAdhocMatchingSetHelloOpt(int matchingId, int optLenAddr, u32 optDataAddr);
|
|
|
|
void __NetAdhocShutdown() {
|
|
// Checks to avoid confusing logspam
|
|
if (netAdhocInited) {
|
|
sceNetAdhocTerm();
|
|
}
|
|
if (netAdhocctlInited) {
|
|
sceNetAdhocctlTerm();
|
|
}
|
|
if (netAdhocMatchingInited) {
|
|
sceNetAdhocMatchingTerm();
|
|
}
|
|
}
|
|
|
|
void __NetAdhocDoState(PointerWrap &p) {
|
|
auto s = p.Section("sceNetAdhoc", 1);
|
|
if (!s)
|
|
return;
|
|
|
|
p.Do(netAdhocInited);
|
|
p.Do(netAdhocctlInited);
|
|
p.Do(netAdhocMatchingInited);
|
|
p.Do(adhocctlHandlers);
|
|
}
|
|
|
|
static void __UpdateAdhocctlHandlers(int flag, int error) {
|
|
u32 args[3] = { 0, 0, 0 };
|
|
args[0] = flag;
|
|
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);
|
|
}
|
|
}
|
|
|
|
static int getBlockingFlag(int id) {
|
|
#ifdef _MSC_VER
|
|
return 0;
|
|
#else
|
|
int sockflag = fcntl(id, F_GETFL, O_NONBLOCK);
|
|
return sockflag & O_NONBLOCK;
|
|
#endif
|
|
}
|
|
|
|
static void __handlerUpdateCallback(u64 userdata, int cycleslate) {
|
|
int buff[2];
|
|
split64(userdata,buff);
|
|
__UpdateAdhocctlHandlers(buff[0], buff[1]);
|
|
}
|
|
|
|
static 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;
|
|
|
|
// 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;
|
|
} else if (!g_Config.bEnableWlan) {
|
|
// Pretend success but don't actually start the friendfinder thread and stuff.
|
|
// Dunno if this is the way to go...
|
|
netAdhocctlInited = true;
|
|
return 0;
|
|
} else if (initNetwork((SceNetAdhocctlAdhocId *)Memory::GetPointer(productAddr)) == 0) {
|
|
netAdhocctlInited = true;
|
|
eventHandlerUpdate = CoreTiming::RegisterEvent("HandlerUpdateEvent", __handlerUpdateCallback);
|
|
friendFinderRunning = true;
|
|
friendFinderThread = std::thread(friendFinder);
|
|
} else {
|
|
//WARN_LOG(SCENET, "sceNetAdhocctlInit: Failed to initialize");
|
|
//return -1; // ERROR_NET_ADHOCCTL_NOT_INITIALIZED; //returning success while initNetwork failed to connect to ProServer may cause some games (ie. GTA:VCS) to behave strangely/stuck, but if not success some games (ie. Ford Street Racing) will also stuck
|
|
WARN_LOG(SCENET, "sceNetAdhocctlInit: Faking success");
|
|
//return 0; // Generic error, but just return success to make games conform.
|
|
}
|
|
netAdhocctlInited = true; //needed for cleanup during ctlTerm 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(%p, %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 = (int)INVALID_SOCKET;
|
|
usocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
// Valid Socket produced
|
|
if (usocket != INVALID_SOCKET) {
|
|
// Enable Port Re-use
|
|
//setsockopt(usocket, SOL_SOCKET, SO_REUSEADDR, &_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;
|
|
|
|
addr.sin_port = htons(port); // This not safe in any way...
|
|
// The port might be under 1024 (ie. GTA:VCS use port 1, Ford Street Racing use port 0 (UNUSED_PORT), etc) and already used by other application/host OS, should we add 1024 to the port whenever it tried to use an already used port?
|
|
|
|
// Bound Socket to local Port
|
|
int iResult = bind(usocket, (sockaddr *)&addr, sizeof(addr));
|
|
/*if (iResult == SOCKET_ERROR && errno == 10048) { //Forcing PdpCreate to be successfull using a different Port might not works and might affect other players sharing the same IP due to "deleteFriendByIP" (should delete by MAC instead of IP)
|
|
addr.sin_port = 0; //UNUSED_PORT
|
|
iResult = bind(usocket, (sockaddr *)&addr, sizeof(addr));
|
|
WARN_LOG(SCENET, "Port %u is already used, replaced with UNUSED_PORT(%u)", port, ntohs(addr.sin_port));
|
|
}*/
|
|
if (iResult == 0) {
|
|
// Allocate Memory for Internal Data
|
|
SceNetAdhocPdpStat * internal = (SceNetAdhocPdpStat *)malloc(sizeof(SceNetAdhocPdpStat));
|
|
|
|
// Allocated Memory
|
|
if (internal != NULL) {
|
|
// Clear Memory
|
|
memset(internal, 0, sizeof(SceNetAdhocPdpStat));
|
|
|
|
// Find Free Translator Index
|
|
int i = 0; for (; i < 255; i++) if (pdp[i] == NULL) break;
|
|
|
|
// Found Free Translator Index
|
|
if (i < 255) {
|
|
// Fill in Data
|
|
internal->id = usocket;
|
|
internal->laddr = *saddr;
|
|
internal->lport = ntohs(getLocalPort(usocket)); // port; //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
|
|
|
|
// Wait for Status to be connected to prevent Wipeout Pulse from showing "WLAN is turned off"
|
|
if ((i == 0) && friendFinderRunning) {
|
|
int cnt = 0;
|
|
while ((threadStatus != ADHOCCTL_STATE_CONNECTED) && (cnt < 5000)) {
|
|
sleep_ms(1);
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
// 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(%u)",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, %p, %i, %p, %i, %i, %i)", id, mac, port, data, len, timeout, flag);
|
|
if (!g_Config.bEnableWlan) {
|
|
return -1;
|
|
}
|
|
SceNetEtherAddr * daddr = (SceNetEtherAddr *)mac;
|
|
uint16 dport = (uint16)port;
|
|
|
|
// Really should flatten this with early outs, all this indentation is making me dizzy.
|
|
|
|
// Library is initialized
|
|
if (netAdhocInited) {
|
|
// Valid Port
|
|
if (dport != 0) {
|
|
// Valid Data Length
|
|
if (len >= 0) { // should we allow 0 size packet (for ping) ?
|
|
// Valid Socket ID
|
|
if (id > 0 && id <= 255 && pdp[id - 1] != NULL) {
|
|
// Cast Socket
|
|
SceNetAdhocPdpStat * socket = pdp[id - 1];
|
|
|
|
// Valid Data Buffer
|
|
if (data != NULL) {
|
|
// Valid Destination Address
|
|
if (daddr != NULL) {
|
|
// Log Destination
|
|
// Schedule Timeout Removal
|
|
if (flag) timeout = 0;
|
|
|
|
// Apply Send Timeout Settings to Socket
|
|
setsockopt(socket->id, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout));
|
|
|
|
// Single Target
|
|
if (!isBroadcastMAC(daddr)) {
|
|
// Fill in Target Structure
|
|
sockaddr_in target;
|
|
target.sin_family = AF_INET;
|
|
target.sin_port = htons(dport);
|
|
|
|
// Get Peer IP
|
|
if (resolveMAC((SceNetEtherAddr *)daddr, (uint32_t *)&target.sin_addr.s_addr)) {
|
|
// Acquire Network Lock
|
|
//_acquireNetworkLock();
|
|
|
|
// Send Data
|
|
changeBlockingMode(socket->id, flag);
|
|
int sent = sendto(socket->id, (const char *)data, len, 0, (sockaddr *)&target, sizeof(target));
|
|
int error = errno;
|
|
if (sent == SOCKET_ERROR) {
|
|
ERROR_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend [size=%i]", error, 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", socket->id, ntohs(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) {
|
|
// Fill in Target Structure
|
|
sockaddr_in target;
|
|
target.sin_family = AF_INET;
|
|
target.sin_addr.s_addr = peer->ip_addr;
|
|
target.sin_port = htons(dport);
|
|
|
|
// Send Data
|
|
changeBlockingMode(socket->id, flag);
|
|
int sent = sendto(socket->id, (const char *)data, len, 0, (sockaddr *)&target, sizeof(target));
|
|
int error = errno;
|
|
if (sent == SOCKET_ERROR) {
|
|
ERROR_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend[%i](BC) [size=%i]", error, socket->id, 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", socket->id, ntohs(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) {
|
|
DEBUG_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", socket->id, ntohs(getLocalPort(socket->id)), received, sip[0], sip[1], sip[2], sip[3], ntohs(sin.sin_port));
|
|
|
|
// Peer MAC
|
|
SceNetEtherAddr mac;
|
|
|
|
// Find Peer MAC
|
|
if (resolveIP(sin.sin_addr.s_addr, &mac)) {
|
|
// Provide Sender Information
|
|
*saddr = mac;
|
|
*sport = ntohs(sin.sin_port);
|
|
|
|
// Save Length
|
|
*len = received;
|
|
|
|
// Free Network Lock
|
|
//_freeNetworkLock();
|
|
|
|
// Return Success
|
|
return 0;
|
|
}
|
|
INFO_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Unknown Peer %u.%u.%u.%u:%u", socket->id, ntohs(getLocalPort(socket->id)), sip[0], sip[1], sip[2], sip[3], ntohs(sin.sin_port));
|
|
|
|
// Free Network Lock
|
|
//_freeNetworkLock();
|
|
|
|
//Receiving data from unknown peer, ignore it ?
|
|
return 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;
|
|
}
|
|
|
|
// Assuming < 0 for failure, homebrew SDK doesn't have much to say about this one..
|
|
static int sceNetAdhocSetSocketAlert(int id, int flag) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocSetSocketAlert(%d, %d)", id, flag);
|
|
return -1;
|
|
}
|
|
|
|
static int sceNetAdhocPollSocket(u32 socketStructAddr, int count, int timeout, int nonblock) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocPollSocket(%08x, %i, %i, %i)", socketStructAddr, count, timeout, nonblock);
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* 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(%x)", productStructAddr);
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
|
|
// Multithreading Lock
|
|
peerlock.lock();
|
|
|
|
// It seems AdHoc Server always sent the full group list, so we should reset current networks to prevent leaving host to be listed again
|
|
freeGroupsRecursive(networks);
|
|
networks = NULL;
|
|
|
|
// Multithreading Unlock
|
|
peerlock.unlock();
|
|
|
|
// 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) {
|
|
ERROR_LOG(SCENET, "Socket error (%i) when sending", errno);
|
|
threadStatus = ADHOCCTL_STATE_DISCONNECTED;
|
|
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 size, u32 bufAddr) {
|
|
INFO_LOG(SCENET, "sceNetAdhocctlGetScanInfo([%08x]=%i, %08x)", size, Memory::Read_U32(size), bufAddr);
|
|
if (!g_Config.bEnableWlan) {
|
|
return 0;
|
|
}
|
|
int * buflen = (int *)Memory::GetPointer(size);
|
|
SceNetAdhocctlScanInfoEmu * buf = NULL;
|
|
if (Memory::IsValidAddress(bufAddr)) {
|
|
buf = (SceNetAdhocctlScanInfoEmu *)Memory::GetPointer(bufAddr);
|
|
}
|
|
// Library initialized
|
|
if (netAdhocctlInited) {
|
|
|
|
if (!Memory::IsValidAddress(size)) return ERROR_NET_ADHOCCTL_INVALID_ARG;
|
|
|
|
// Minimum Argument Requirements
|
|
if (buflen != NULL) {
|
|
// Multithreading Lock
|
|
peerlock.lock();
|
|
|
|
// Length Returner Mode
|
|
if (buf == NULL) *buflen = countAvailableNetworks() * sizeof(SceNetAdhocctlScanInfoEmu);
|
|
|
|
// Normal Information Mode
|
|
else {
|
|
// Clear Memory
|
|
memset(buf, 0, *buflen);
|
|
|
|
// Network Discovery Counter
|
|
int discovered = 0;
|
|
|
|
// Count requested Networks
|
|
int requestcount = *buflen / sizeof(SceNetAdhocctlScanInfoEmu);
|
|
|
|
// Minimum Argument Requirements
|
|
if (requestcount > 0) {
|
|
// Group List Element
|
|
SceNetAdhocctlScanInfo * group = networks;
|
|
|
|
// Iterate Group List
|
|
for (; group != NULL && discovered < requestcount; group = group->next) {
|
|
// Copy Group Information
|
|
//buf[discovered] = *group;
|
|
buf[discovered].group_name = group->group_name;
|
|
buf[discovered].bssid = group->bssid;
|
|
buf[discovered].mode = group->mode;
|
|
|
|
// Exchange Adhoc Channel
|
|
// sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_ADHOC_CHANNEL, &buf[discovered].channel);
|
|
|
|
// Fake Channel Number 1 on Automatic Channel
|
|
// if (buf[discovered].channel == 0) buf[discovered].channel = 1;
|
|
|
|
//Always Fake Channel 1
|
|
buf[discovered].channel = 1;
|
|
|
|
// Increase Discovery Counter
|
|
discovered++;
|
|
}
|
|
|
|
// Link List
|
|
int i = 0; for (; i < discovered - 1; i++) {
|
|
// Link Network
|
|
buf[i].next = bufAddr + (sizeof(SceNetAdhocctlScanInfoEmu)*i) + sizeof(SceNetAdhocctlScanInfoEmu); // buf[i].next = &buf[i + 1];
|
|
}
|
|
|
|
// Fix Last Element
|
|
if (discovered > 0) buf[discovered - 1].next = 0;
|
|
}
|
|
|
|
// Fix Size
|
|
*buflen = discovered * sizeof(SceNetAdhocctlScanInfoEmu);
|
|
}
|
|
|
|
// Multithreading Unlock
|
|
peerlock.unlock();
|
|
|
|
// Return Success
|
|
return 0;
|
|
}
|
|
|
|
// Generic Error
|
|
return -1;
|
|
}
|
|
|
|
// Library uninitialized
|
|
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
|
|
}
|
|
|
|
// TODO: How many handlers can the PSP actually have for Adhocctl?
|
|
// TODO: Should we allow the same handler to be added more than once?
|
|
static u32 sceNetAdhocctlAddHandler(u32 handlerPtr, u32 handlerArg) {
|
|
bool foundHandler = false;
|
|
u32 retval = 0;
|
|
struct AdhocctlHandler handler;
|
|
memset(&handler, 0, sizeof(handler));
|
|
|
|
while (adhocctlHandlers.find(retval) != adhocctlHandlers.end())
|
|
++retval;
|
|
|
|
handler.entryPoint = handlerPtr;
|
|
handler.argument = handlerArg;
|
|
|
|
for (std::map<int, AdhocctlHandler>::iterator it = adhocctlHandlers.begin(); it != adhocctlHandlers.end(); it++) {
|
|
if (it->second.entryPoint == handlerPtr) {
|
|
foundHandler = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!foundHandler && Memory::IsValidAddress(handlerPtr)) {
|
|
if (adhocctlHandlers.size() >= MAX_ADHOCCTL_HANDLERS) {
|
|
ERROR_LOG(SCENET, "UNTESTED sceNetAdhocctlAddHandler(%x, %x): Too many handlers", handlerPtr, handlerArg);
|
|
retval = ERROR_NET_ADHOCCTL_TOO_MANY_HANDLERS;
|
|
return retval;
|
|
}
|
|
adhocctlHandlers[retval] = handler;
|
|
WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlAddHandler(%x, %x): added handler %d", handlerPtr, handlerArg, retval);
|
|
} else if(foundHandler) {
|
|
ERROR_LOG(SCENET, "UNTESTED sceNetAdhocctlAddHandler(%x, %x): Same handler already exists", handlerPtr, handlerArg);
|
|
retval = 0; //Faking success
|
|
} else {
|
|
ERROR_LOG(SCENET, "UNTESTED sceNetAdhocctlAddHandler(%x, %x): Invalid handler", handlerPtr, handlerArg);
|
|
retval = ERROR_NET_ADHOCCTL_INVALID_ARG;
|
|
}
|
|
|
|
// The id to return is the number of handlers currently registered
|
|
return retval;
|
|
}
|
|
|
|
static u32 sceNetAdhocctlDisconnect() {
|
|
// WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right?
|
|
INFO_LOG(SCENET, "sceNetAdhocctlDisconnect() at %08x", currentMIPS->pc);
|
|
/*
|
|
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)
|
|
__UpdateAdhocctlHandlers(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 (!g_Config.bEnableWlan) {
|
|
netAdhocctlInited = false;
|
|
return 0;
|
|
}
|
|
*/
|
|
|
|
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
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sceNetAdhocctlGetNameByAddr(const char *mac, u32 nameAddr) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlGetNameByAddr(%s, %08x)", mac, nameAddr);
|
|
return -1;
|
|
}
|
|
|
|
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) {
|
|
|
|
if (!Memory::IsValidAddress(scanInfoAddr)) return ERROR_NET_ADHOCCTL_INVALID_ARG;
|
|
|
|
// Disconnected State
|
|
if (threadStatus == ADHOCCTL_STATE_DISCONNECTED) {
|
|
|
|
SceNetAdhocctlScanInfo * sinfo = (SceNetAdhocctlScanInfo*)Memory::GetPointer(scanInfoAddr);
|
|
|
|
// 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 (sinfo != NULL)
|
|
packet.group = sinfo->group_name;
|
|
|
|
// 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_DISCONNECTED; // ERROR_NET_ADHOCCTL_BUSY;
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
|
|
}
|
|
|
|
static int sceNetAdhocctlGetPeerInfo(const char *mac, int size, u32 peerInfoAddr) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlGetPeerInfo(%s, %i, %08x)", mac, size, peerInfoAddr);
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* 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_DISCONNECTED; // ERROR_NET_ADHOCCTL_BUSY;
|
|
}
|
|
|
|
// 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));
|
|
} else {
|
|
return ERROR_NET_ADHOC_INVALID_ADDR; // shouldn't this be ERROR_NET_ADHOC_INVALID_ARG ?
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 (!g_Config.bEnableWlan) {
|
|
netAdhocInited = false;
|
|
return 0;
|
|
}
|
|
*/
|
|
|
|
// Library is initialized
|
|
if (netAdhocInited) {
|
|
// 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(int structSize, u32 structAddr) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGetPdpStat(%i, %08x)", structSize, structAddr);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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(%u,%u)",structSize,structAddr);
|
|
|
|
if (!g_Config.bEnableWlan) {
|
|
return 0;
|
|
}
|
|
|
|
int * buflen = (int *)Memory::GetPointer(structSize);
|
|
// Library is initialized
|
|
if (netAdhocInited) {
|
|
// Length Returner Mode
|
|
if (buflen != NULL && !Memory::IsValidAddress(structAddr)) {
|
|
// Return Required Size
|
|
*buflen = sizeof(SceNetAdhocPtpStat) * getPTPSocketCount();
|
|
|
|
// Success
|
|
return 0;
|
|
}
|
|
|
|
// Status Returner Mode
|
|
else if (buflen != NULL && Memory::IsValidAddress(structAddr)) {
|
|
// Socket Count
|
|
int socketcount = getPTPSocketCount();
|
|
SceNetAdhocPtpStat * buf = (SceNetAdhocPtpStat *)Memory::GetPointer(structAddr);
|
|
|
|
// 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*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) {
|
|
DEBUG_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 = (int)INVALID_SOCKET;
|
|
tcpsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
|
// Valid Socket produced
|
|
if (tcpsocket > 0) {
|
|
// Enable Port Re-use
|
|
setsockopt(tcpsocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&one, sizeof(one));
|
|
|
|
// Binding Information for local Port
|
|
sockaddr_in addr;
|
|
// addr.sin_len = sizeof(addr);
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = INADDR_ANY;
|
|
addr.sin_port = htons(sport);
|
|
|
|
// Bound Socket to local Port
|
|
if (bind(tcpsocket, (sockaddr *)&addr, sizeof(addr)) == 0) {
|
|
// Update sport with the port assigned by bind
|
|
socklen_t len = sizeof(addr);
|
|
if (getsockname(tcpsocket, (sockaddr *)&addr, &len) == 0) {
|
|
sport = ntohs(addr.sin_port);
|
|
}
|
|
|
|
// Allocate Memory
|
|
SceNetAdhocPtpStat * internal = (SceNetAdhocPtpStat *)malloc(sizeof(SceNetAdhocPtpStat));
|
|
|
|
// Allocated Memory
|
|
if (internal != NULL) {
|
|
// Find Free Translator ID
|
|
int i = 0; for (; i < 255; i++) if (ptp[i] == NULL) break;
|
|
|
|
// Found Free Translator ID
|
|
if (i < 255) {
|
|
// Clear Memory
|
|
memset(internal, 0, sizeof(SceNetAdhocPtpStat));
|
|
|
|
// Copy Infrastructure Socket ID
|
|
internal->id = tcpsocket;
|
|
|
|
// Copy Address Information
|
|
internal->laddr = *saddr;
|
|
internal->paddr = *daddr;
|
|
internal->lport = sport;
|
|
internal->pport = dport;
|
|
|
|
// Set Buffer Size
|
|
internal->rcv_sb_cc = bufsize;
|
|
|
|
// Link PTP Socket
|
|
ptp[i] = internal;
|
|
|
|
// Add Port Forward to Router
|
|
// sceNetPortOpen("TCP", sport);
|
|
|
|
// Return PTP Socket Pointer
|
|
return i + 1;
|
|
}
|
|
|
|
// Free Memory
|
|
free(internal);
|
|
}
|
|
}
|
|
|
|
// Close Socket
|
|
closesocket(tcpsocket);
|
|
}
|
|
}
|
|
|
|
// Invalid Arguments
|
|
return ERROR_NET_ADHOC_INVALID_ARG;
|
|
}
|
|
|
|
// Invalid Ports
|
|
return ERROR_NET_ADHOC_INVALID_PORT;
|
|
}
|
|
|
|
// Invalid Addresses
|
|
return ERROR_NET_ADHOC_INVALID_ADDR;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Adhoc Emulator PTP Connection Acceptor
|
|
* @param id Socket File Descriptor
|
|
* @param addr OUT: Peer MAC Address
|
|
* @param port OUT: Peer Port
|
|
* @param timeout Accept Timeout (in Microseconds)
|
|
* @param flag Nonblocking Flag
|
|
* @return Socket ID >= 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_SOCKET_ALERTED, ADHOC_SOCKET_ID_NOT_AVAIL, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_NOT_LISTENED, ADHOC_THREAD_ABORTED, NET_INTERNAL
|
|
*/
|
|
static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int timeout, int flag) {
|
|
|
|
SceNetEtherAddr * addr = NULL;
|
|
if (Memory::IsValidAddress(peerMacAddrPtr)) {
|
|
addr = PSPPointer<SceNetEtherAddr>::Create(peerMacAddrPtr);
|
|
}
|
|
uint16_t * port = NULL; //
|
|
if (Memory::IsValidAddress(peerPortPtr)) {
|
|
port = (uint16_t *)Memory::GetPointer(peerPortPtr);
|
|
}
|
|
DEBUG_LOG(SCENET, "sceNetAdhocPtpAccept(%d,%08x,[%08x]=%u,%d,%u)", id, peerMacAddrPtr, peerPortPtr, port ? *port : -1, timeout, flag);
|
|
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 == PTP_STATE_LISTEN) {
|
|
// Valid Arguments
|
|
if (addr != NULL && port != NULL) {
|
|
// 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()*1000.0);
|
|
|
|
// Retry until Timeout hits
|
|
while ((timeout == 0 ||((uint32_t)(real_time_now()*1000.0) - starttime) < (uint32_t)timeout) && newsocket == -1) {
|
|
// Accept Connection
|
|
newsocket = accept(socket->id, (sockaddr *)&peeraddr, &peeraddrlen);
|
|
|
|
// Wait a bit...
|
|
sleep_ms(1);
|
|
}
|
|
}
|
|
|
|
// Restore Blocking Behaviour
|
|
if (nbio == 0) {
|
|
// Restore Socket Option
|
|
changeBlockingMode(socket->id,0);
|
|
}
|
|
|
|
// Accepted New Connection
|
|
if (newsocket > 0) {
|
|
// 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;
|
|
|
|
// Copy Local Address Data to Structure
|
|
getLocalMac(&internal->laddr);
|
|
internal->lport = ntohs(local.sin_port);
|
|
|
|
// Copy Peer Address Data to Structure
|
|
internal->paddr = mac;
|
|
internal->pport = ntohs(peeraddr.sin_port);
|
|
|
|
// Set Connected State
|
|
internal->state = PTP_STATE_ESTABLISHED;
|
|
|
|
// Return Peer Address Information
|
|
*addr = internal->paddr;
|
|
*port = internal->pport;
|
|
|
|
// Link PTP Socket
|
|
ptp[i] = internal;
|
|
|
|
// Add Port Forward to Router
|
|
// sceNetPortOpen("TCP", internal->lport);
|
|
|
|
// Return Socket
|
|
return i + 1;
|
|
}
|
|
|
|
// Free Memory
|
|
free(internal);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Close Socket
|
|
closesocket(newsocket);
|
|
}
|
|
|
|
// 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) {
|
|
DEBUG_LOG(SCENET, "sceNetAdhocPtpConnect(%i, %i, %08x)", id, timeout, flag);
|
|
if (!g_Config.bEnableWlan) {
|
|
return 0;
|
|
}
|
|
|
|
// Library is initialized
|
|
if (netAdhocInited)
|
|
{
|
|
// Valid Socket
|
|
if (id > 0 && id <= 255 && ptp[id - 1] != NULL) {
|
|
// Cast Socket
|
|
SceNetAdhocPtpStat * socket = ptp[id - 1];
|
|
|
|
// Valid Client Socket
|
|
if (socket->state == 0) {
|
|
// Target Address
|
|
sockaddr_in sin;
|
|
memset(&sin, 0, sizeof(sin));
|
|
|
|
// Setup Target Address
|
|
// sin.sin_len = sizeof(sin);
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_port = htons(socket->pport);
|
|
|
|
// Grab Peer IP
|
|
if (resolveMAC(&socket->paddr, (uint32_t *)&sin.sin_addr.s_addr)) {
|
|
// Grab Nonblocking Flag
|
|
uint32_t nbio = getBlockingFlag(socket->id);
|
|
// Switch to Nonblocking Behaviour
|
|
if (nbio == 0) {
|
|
// Overwrite Socket Option
|
|
changeBlockingMode(socket->id, 1);
|
|
}
|
|
|
|
// Connect Socket to Peer (Nonblocking)
|
|
int connectresult = connect(socket->id, (sockaddr *)&sin, sizeof(sin));
|
|
|
|
// Grab Error Code
|
|
int errorcode = errno;
|
|
|
|
// Restore Blocking Behaviour
|
|
if (nbio == 0) {
|
|
// Restore Socket Option
|
|
changeBlockingMode(socket->id,0);
|
|
}
|
|
|
|
// Instant Connection (Lucky!)
|
|
if (connectresult == 0 || (connectresult == -1 && errorcode == EISCONN)) {
|
|
// Set Connected State
|
|
socket->state = PTP_STATE_ESTABLISHED;
|
|
|
|
// 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()*1000.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()*1000.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 = PTP_STATE_ESTABLISHED;
|
|
|
|
// 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) {
|
|
DEBUG_LOG(SCENET,"sceNetAdhocPtpClose(%d,%d)",id,unknown);
|
|
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) {
|
|
DEBUG_LOG(SCENET, "sceNetAdhocPtpListen(%p,%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) {
|
|
// 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);
|
|
|
|
// Bound Socket to local Port
|
|
if (bind(tcpsocket, (sockaddr *)&addr, sizeof(addr)) == 0) {
|
|
// Switch into Listening Mode
|
|
if (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 = 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Close Socket
|
|
closesocket(tcpsocket);
|
|
}
|
|
|
|
// 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)", id, dataAddr, dataSizeAddr, timeout, flag);
|
|
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 == 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;
|
|
|
|
// 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 = 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 == 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;
|
|
|
|
// 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 = 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)", id, timeout, nonblock);
|
|
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;
|
|
}
|
|
|
|
static int sceNetAdhocGetSocketAlert() {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGetSocketAlert()");
|
|
return 0;
|
|
}
|
|
|
|
static int sceNetAdhocMatchingInit(u32 memsize) {
|
|
INFO_LOG(SCENET, "sceNetAdhocMatchingInit(%d) at %08x", memsize, currentMIPS->pc);
|
|
// Uninitialized Library
|
|
if (!netAdhocMatchingInited) {
|
|
// Save Fake Pool Size
|
|
fakePoolSize = memsize;
|
|
|
|
// Initialize Library
|
|
netAdhocMatchingInited = true;
|
|
|
|
// Return Success
|
|
return 0;
|
|
} else {
|
|
return ERROR_NET_ADHOC_MATCHING_ALREADY_INITIALIZED;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
// Should we also delete all PDP? since creating a matching context will also create a PDP socket
|
|
|
|
}
|
|
*/
|
|
ERROR_LOG(SCENET, "UNIMPL 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) {
|
|
INFO_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->hello_int = hello_int;
|
|
context->keepalive_int = 500000;
|
|
//context->keepalive_int = keepalive_int;
|
|
context->resendcounter = init_count;
|
|
context->keepalivecounter = 100;
|
|
//context->keepalivecounter = init_count;
|
|
context->resend_int = rexmt_int;
|
|
context->handler = handler;
|
|
|
|
// Fill in Selfpeer
|
|
context->mac = localmac;
|
|
|
|
// Multithreading Lock
|
|
peerlock.lock(); //contextlock.lock();
|
|
|
|
// 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) {
|
|
ERROR_LOG(SCENET, "UNIMPL 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;
|
|
|
|
SceNetAdhocMatchingContext * item = findMatchingContext(matchingId);
|
|
if (item != NULL) {
|
|
sceNetAdhocMatchingSetHelloOpt(matchingId, optLen, optDataAddr);
|
|
if ((optLen > 0) && Memory::IsValidAddress(optDataAddr)) {
|
|
// Allocate the memory and copy the content
|
|
if (item->hello != NULL) free(item->hello);
|
|
item->hello = malloc(optLen);
|
|
if (item->hello != NULL)
|
|
Memory::Memcpy(item->hello, optDataAddr, optLen);
|
|
}
|
|
//else return ERROR_NET_ADHOC_MATCHING_INVALID_ARG; // ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN; // Returning Not Success will cause GTA:VC unable to host/join
|
|
|
|
//Add your own MAC as a member (ony if it's empty?)
|
|
addMember(item, &item->mac);
|
|
|
|
//TODO: Create a new thread
|
|
// Easier to implement if using an existing thread (friendFinder) instead of creating a new one ^_^
|
|
|
|
//sceKernelCreateThread
|
|
/*eventHandlerUpdate = CoreTiming::RegisterEvent("HandlerUpdateEvent", __handlerUpdateCallback);
|
|
memberFinderRunning = true;
|
|
memberFinderThread = std::thread(memberFinder);*/
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sceNetAdhocMatchingStop(int matchingId) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocMatchingStop(%i)", matchingId);
|
|
if (!g_Config.bEnableWlan)
|
|
return -1;
|
|
|
|
SceNetAdhocMatchingContext * item = findMatchingContext(matchingId);
|
|
if (item != NULL) {
|
|
/*memberFinderRunning = false;
|
|
if (memberFinderThread.joinable()) {
|
|
friendFinderThread.join();
|
|
}*/
|
|
|
|
// Remove your own MAC, or All memebers, or don't remove at all or we should do this on MatchingDelete ?
|
|
//deleteAllMembers(item);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sceNetAdhocMatchingDelete(int matchingId) {
|
|
// WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right?
|
|
/*
|
|
if (!g_Config.bEnableWlan)
|
|
return -1;
|
|
*/
|
|
|
|
// Previous Context Reference
|
|
SceNetAdhocMatchingContext * prev = NULL;
|
|
|
|
// Context Pointer
|
|
SceNetAdhocMatchingContext * item = contexts;
|
|
|
|
// Iterate contexts
|
|
for (; item != NULL; item = item->next) {
|
|
// Found matching ID
|
|
if (item->id == matchingId) {
|
|
// Multithreading Lock
|
|
peerlock.lock(); //contextlock.lock();
|
|
|
|
// Unlink Left (Beginning)
|
|
if (prev == NULL) contexts = item->next;
|
|
|
|
// Unlink Left (Other)
|
|
else prev->next = item->next;
|
|
|
|
// Multithreading Unlock
|
|
peerlock.unlock(); //contextlock.unlock();
|
|
|
|
// Delete the socket
|
|
sceNetAdhocPdpDelete(item->socket, 0); // item->connected = (sceNetAdhocPdpDelete(item->socket, 0) < 0);
|
|
// Free allocated memories
|
|
free(item->hello);
|
|
free(item->rxbuf);
|
|
deleteAllMembers(item);
|
|
// Free item context memory
|
|
free(item);
|
|
|
|
// Stop Search
|
|
break;
|
|
}
|
|
|
|
// Set Previous Reference
|
|
prev = item;
|
|
}
|
|
|
|
WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingDelete(%i) at %08x", matchingId, currentMIPS->pc);
|
|
return 0;
|
|
}
|
|
|
|
static int sceNetAdhocMatchingSelectTarget(int matchingId, const char *macAddress, int optLen, u32 optDataPtr) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocMatchingSelectTarget(%i, %s, %i, %08x)", matchingId, macAddress, optLen, optDataPtr);
|
|
if (!g_Config.bEnableWlan)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int sceNetAdhocMatchingCancelTargetWithOpt(int matchingId, const char *macAddress, int optLen, u32 optDataPtr) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocMatchingCancelTargetWithOpt(%i, %s, %i, %08x)", matchingId, macAddress, optLen, optDataPtr);
|
|
if (!g_Config.bEnableWlan)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int sceNetAdhocMatchingCancelTarget(int matchingId, const char *macAddress) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocMatchingCancelTarget(%i, %s)", matchingId, macAddress);
|
|
if (!g_Config.bEnableWlan)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int sceNetAdhocMatchingGetHelloOpt(int matchingId, u32 optLenAddr, u32 optDataAddr) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocMatchingGetHelloOpt(%i, %08x, %08x)", matchingId, optLenAddr, optDataAddr);
|
|
if (!g_Config.bEnableWlan)
|
|
return -1;
|
|
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 (!Memory::IsValidAddress(optDataAddr)) return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
|
|
|
|
SceNetAdhocMatchingContext * item = findMatchingContext(matchingId);
|
|
if (item != NULL) {
|
|
// Set OptData
|
|
int oldLen = item->hellolen;
|
|
item->hellolen = optLenAddr; //optLenAddr doesn't seems to be an address to the actual len
|
|
item->helloAddr = optDataAddr;
|
|
|
|
if (optLenAddr <= 0 || !Memory::IsValidAddress(optDataAddr)) {
|
|
if (item->hello != NULL) {
|
|
free(item->hello);
|
|
item->hello = NULL;
|
|
}
|
|
}
|
|
else {
|
|
if (optLenAddr > oldLen) {
|
|
free(item->hello);
|
|
item->hello = malloc(optLenAddr);
|
|
}
|
|
Memory::Memcpy(item->hello, optDataAddr, optLenAddr);
|
|
}
|
|
}
|
|
//else return ERROR_NET_ADHOC_MATCHING_INVALID_ID;
|
|
|
|
//sceNetAdhocctlScan();
|
|
|
|
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;
|
|
|
|
// Minimum Argument
|
|
if (!Memory::IsValidAddress(sizeAddr)) return ERROR_NET_ADHOC_MATCHING_INVALID_ARG;
|
|
|
|
SceNetAdhocMatchingContext * item = findMatchingContext(matchingId);
|
|
if (item != NULL) {
|
|
int * buflen = (int *)Memory::GetPointer(sizeAddr);
|
|
SceNetAdhocMatchingMemberInfoEmu * buf2 = NULL;
|
|
if (Memory::IsValidAddress(buf)) {
|
|
buf2 = (SceNetAdhocMatchingMemberInfoEmu *)Memory::GetPointer(buf);
|
|
}
|
|
|
|
// Multithreading Lock
|
|
peerlock.lock();
|
|
|
|
// Iterate Members
|
|
//SceNetAdhocMatchingMemberInternal * peer = item->peerlist;
|
|
|
|
//Using friend list instead, since it's easier to implement ^_^
|
|
SceNetAdhocctlPeerInfo * peer = friends;
|
|
|
|
int count = 0;
|
|
if (!Memory::IsValidAddress(buf)) { // Returning needed buf size instead
|
|
count++;
|
|
for (; peer != NULL; peer = peer->next) count++;
|
|
}
|
|
else {
|
|
buf2[count].mac_addr = item->mac; //add your own MAC
|
|
buf2[count].padding[0] = 0; // does padding need to be 0 ?
|
|
buf2[count++].padding[1] = 0;
|
|
for (; peer != NULL; peer = peer->next) {
|
|
buf2[count].mac_addr = peer->mac_addr;
|
|
buf2[count].padding[0] = 0;
|
|
buf2[count].padding[1] = 0;
|
|
count++;
|
|
}
|
|
// Link List
|
|
int i = 0;
|
|
for (; i < count - 1; i++) {
|
|
// Link Network
|
|
buf2[i].next = buf + (sizeof(SceNetAdhocMatchingMemberInfoEmu)*i) + sizeof(SceNetAdhocMatchingMemberInfoEmu);
|
|
}
|
|
// Fix Last Element
|
|
if (count > 0) buf2[count - 1].next = 0;
|
|
}
|
|
|
|
// Fix Size
|
|
*buflen = count * sizeof(SceNetAdhocMatchingMemberInfoEmu); //includes your own MAC
|
|
|
|
// Multithreading Unlock
|
|
peerlock.unlock();
|
|
}
|
|
//else return ERROR_NET_ADHOC_MATCHING_INVALID_ID;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sceNetAdhocMatchingSendData(int matchingId, const char *mac, int dataLen, u32 dataAddr) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocMatchingSendData(%i, %s, %i, %08x)", matchingId, mac, dataLen, dataAddr);
|
|
if (!g_Config.bEnableWlan)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int sceNetAdhocMatchingAbortSendData(int matchingId, const char *mac) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocMatchingAbortSendData(%i, %s)", matchingId, mac);
|
|
if (!g_Config.bEnableWlan)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int sceNetAdhocMatchingGetPoolMaxAlloc() {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocMatchingGetPoolMaxAlloc()");
|
|
if (!g_Config.bEnableWlan)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int sceNetAdhocMatchingGetPoolStat() {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocMatchingGetPoolStat()");
|
|
if (!g_Config.bEnableWlan)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
const HLEFunction sceNetAdhoc[] = {
|
|
{0xE1D621D7, WrapU_V<sceNetAdhocInit>, "sceNetAdhocInit"},
|
|
{0xA62C6F57, WrapI_V<sceNetAdhocTerm>, "sceNetAdhocTerm"},
|
|
{0x0AD043ED, WrapI_U<sceNetAdhocctlConnect>, "sceNetAdhocctlConnect"},
|
|
{0x6f92741b, WrapI_CUIU<sceNetAdhocPdpCreate>, "sceNetAdhocPdpCreate"},
|
|
{0xabed3790, WrapI_ICUVIII<sceNetAdhocPdpSend>, "sceNetAdhocPdpSend"},
|
|
{0xdfe53e03, WrapI_IVVVVUI<sceNetAdhocPdpRecv>, "sceNetAdhocPdpRecv"},
|
|
{0x7f27bb5e, WrapI_II<sceNetAdhocPdpDelete>, "sceNetAdhocPdpDelete"},
|
|
{0xc7c1fc57, WrapI_IU<sceNetAdhocGetPdpStat>, "sceNetAdhocGetPdpStat"},
|
|
{0x157e6225, WrapI_II<sceNetAdhocPtpClose>, "sceNetAdhocPtpClose"},
|
|
{0x4da4c788, WrapI_IUUII<sceNetAdhocPtpSend>, "sceNetAdhocPtpSend"},
|
|
{0x877f6d66, WrapI_CICIIIII<sceNetAdhocPtpOpen>, "sceNetAdhocPtpOpen"},
|
|
{0x8bea2b3e, WrapI_IUUII<sceNetAdhocPtpRecv>, "sceNetAdhocPtpRecv"},
|
|
{0x9df81198, WrapI_IUUII<sceNetAdhocPtpAccept>, "sceNetAdhocPtpAccept"},
|
|
{0xe08bdac1, WrapI_CIIIIII<sceNetAdhocPtpListen>, "sceNetAdhocPtpListen"},
|
|
{0xfc6fc07b, WrapI_III<sceNetAdhocPtpConnect>, "sceNetAdhocPtpConnect"},
|
|
{0x9ac2eeac, WrapI_III<sceNetAdhocPtpFlush>, "sceNetAdhocPtpFlush"},
|
|
{0xb9685118, WrapI_UU<sceNetAdhocGetPtpStat>, "sceNetAdhocGetPtpStat"},
|
|
{0x3278ab0c, WrapI_CUI<sceNetAdhocGameModeCreateReplica>, "sceNetAdhocGameModeCreateReplica"},
|
|
{0x98c204c8, WrapI_V<sceNetAdhocGameModeUpdateMaster>, "sceNetAdhocGameModeUpdateMaster"},
|
|
{0xfa324b4e, WrapI_IU<sceNetAdhocGameModeUpdateReplica>, "sceNetAdhocGameModeUpdateReplica"},
|
|
{0xa0229362, WrapI_V<sceNetAdhocGameModeDeleteMaster>, "sceNetAdhocGameModeDeleteMaster"},
|
|
{0x0b2228e9, WrapI_I<sceNetAdhocGameModeDeleteReplica>, "sceNetAdhocGameModeDeleteReplica"},
|
|
{0x7F75C338, WrapI_UI<sceNetAdhocGameModeCreateMaster>, "sceNetAdhocGameModeCreateMaster"},
|
|
{0x73bfd52d, WrapI_II<sceNetAdhocSetSocketAlert>, "sceNetAdhocSetSocketAlert"},
|
|
{0x4d2ce199, WrapI_V<sceNetAdhocGetSocketAlert>, "sceNetAdhocGetSocketAlert"},
|
|
{0x7a662d6b, WrapI_UIII<sceNetAdhocPollSocket>, "sceNetAdhocPollSocket"},
|
|
};
|
|
|
|
const HLEFunction sceNetAdhocMatching[] = {
|
|
{0x2a2a1e07, WrapI_U<sceNetAdhocMatchingInit>, "sceNetAdhocMatchingInit"},
|
|
{0x7945ecda, WrapI_V<sceNetAdhocMatchingTerm>, "sceNetAdhocMatchingTerm"},
|
|
{0xca5eda6f, WrapI_IIIIIIIIU<sceNetAdhocMatchingCreate>, "sceNetAdhocMatchingCreate"},
|
|
{0x93ef3843, WrapI_IIIIIIU<sceNetAdhocMatchingStart>, "sceNetAdhocMatchingStart"},
|
|
{0x32b156b3, WrapI_I<sceNetAdhocMatchingStop>, "sceNetAdhocMatchingStop"},
|
|
{0xf16eaf4f, WrapI_I<sceNetAdhocMatchingDelete>, "sceNetAdhocMatchingDelete"},
|
|
{0x5e3d4b79, WrapI_ICIU<sceNetAdhocMatchingSelectTarget>, "sceNetAdhocMatchingSelectTarget"},
|
|
{0xea3c6108, WrapI_IC<sceNetAdhocMatchingCancelTarget>, "sceNetAdhocMatchingCancelTarget"},
|
|
{0x8f58bedf, WrapI_ICIU<sceNetAdhocMatchingCancelTargetWithOpt>, "sceNetAdhocMatchingCancelTargetWithOpt"},
|
|
{0xb5d96c2a, WrapI_IUU<sceNetAdhocMatchingGetHelloOpt>, "sceNetAdhocMatchingGetHelloOpt"},
|
|
{0xb58e61b7, WrapI_IIU<sceNetAdhocMatchingSetHelloOpt>, "sceNetAdhocMatchingSetHelloOpt"},
|
|
{0xc58bcd9e, WrapI_IUU<sceNetAdhocMatchingGetMembers>, "sceNetAdhocMatchingGetMembers"},
|
|
{0xf79472d7, WrapI_ICIU<sceNetAdhocMatchingSendData>, "sceNetAdhocMatchingSendData"},
|
|
{0xec19337d, WrapI_IC<sceNetAdhocMatchingAbortSendData>, "sceNetAdhocMatchingAbortSendData"},
|
|
{0x40F8F435, WrapI_V<sceNetAdhocMatchingGetPoolMaxAlloc>, "sceNetAdhocMatchingGetPoolMaxAlloc"},
|
|
{0x9c5cfb7d, WrapI_V<sceNetAdhocMatchingGetPoolStat>, "sceNetAdhocMatchingGetPoolStat"},
|
|
};
|
|
|
|
static int sceNetAdhocctlExitGameMode() {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlExitGameMode()");
|
|
return -1;
|
|
}
|
|
|
|
static int sceNetAdhocctlGetGameModeInfo(u32 infoAddr) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlGetGameModeInfo(%08x)", infoAddr);
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) {
|
|
INFO_LOG(SCENET, "sceNetAdhocctlGetPeerList(%08x, %08x) at %08x", sizeAddr, bufAddr, currentMIPS->pc);
|
|
if (!g_Config.bEnableWlan) {
|
|
return -1;
|
|
}
|
|
|
|
int * buflen = (int *)Memory::GetPointer(sizeAddr);
|
|
SceNetAdhocctlPeerInfoEmu * buf = NULL;
|
|
if (Memory::IsValidAddress(bufAddr)) {
|
|
buf = (SceNetAdhocctlPeerInfoEmu *)Memory::GetPointer(bufAddr);
|
|
}
|
|
// 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
|
|
peer->last_recv = (uint64_t)time(NULL);
|
|
|
|
// 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(SceNetAdhocctlPeerInfo);
|
|
}
|
|
|
|
// 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) {
|
|
ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlGetPeerList(%s, %08x, %08x)", nickName, sizeAddr, bufAddr);
|
|
return -1;
|
|
}
|
|
|
|
const HLEFunction sceNetAdhocctl[] = {
|
|
{0xE26F226E, WrapU_IIU<sceNetAdhocctlInit>, "sceNetAdhocctlInit"},
|
|
{0x9D689E13, WrapI_V<sceNetAdhocctlTerm>, "sceNetAdhocctlTerm"},
|
|
{0x20B317A0, WrapU_UU<sceNetAdhocctlAddHandler>, "sceNetAdhocctlAddHandler"},
|
|
{0x6402490B, WrapU_U<sceNetAdhocctlDelHandler>, "sceNetAdhocctlDelHandler"},
|
|
{0x34401D65, WrapU_V<sceNetAdhocctlDisconnect>, "sceNetAdhocctlDisconnect"},
|
|
{0x0ad043ed, WrapI_U<sceNetAdhocctlConnect>, "sceNetAdhocctlConnect"},
|
|
{0x08fff7a0, WrapI_V<sceNetAdhocctlScan>, "sceNetAdhocctlScan"},
|
|
{0x75ecd386, WrapI_U<sceNetAdhocctlGetState>, "sceNetAdhocctlGetState"},
|
|
{0x8916c003, WrapI_CU<sceNetAdhocctlGetNameByAddr>, "sceNetAdhocctlGetNameByAddr"},
|
|
{0xded9d28e, WrapI_U<sceNetAdhocctlGetParameter>, "sceNetAdhocctlGetParameter"},
|
|
{0x81aee1be, WrapI_UU<sceNetAdhocctlGetScanInfo>, "sceNetAdhocctlGetScanInfo"},
|
|
{0x5e7f79c9, WrapI_U<sceNetAdhocctlJoin>, "sceNetAdhocctlJoin"},
|
|
{0x8db83fdc, WrapI_CIU<sceNetAdhocctlGetPeerInfo>, "sceNetAdhocctlGetPeerInfo"},
|
|
{0xec0635c1, WrapI_C<sceNetAdhocctlCreate>, "sceNetAdhocctlCreate"},
|
|
{0xa5c055ce, WrapI_CIIUII<sceNetAdhocctlCreateEnterGameMode>, "sceNetAdhocctlCreateEnterGameMode"},
|
|
{0x1ff89745, WrapI_CCII<sceNetAdhocctlJoinEnterGameMode>, "sceNetAdhocctlJoinEnterGameMode"},
|
|
{0xcf8e084d, WrapI_V<sceNetAdhocctlExitGameMode>, "sceNetAdhocctlExitGameMode"},
|
|
{0xe162cb14, WrapI_UU<sceNetAdhocctlGetPeerList>, "sceNetAdhocctlGetPeerList"},
|
|
{0x362cbe8f, WrapI_U<sceNetAdhocctlGetAdhocId>, "sceNetAdhocctlGetAdhocId"},
|
|
{0x5a014ce0, WrapI_U<sceNetAdhocctlGetGameModeInfo>, "sceNetAdhocctlGetGameModeInfo"},
|
|
{0x99560abe, WrapI_CUU<sceNetAdhocctlGetAddrByName>, "sceNetAdhocctlGetAddrByName"},
|
|
{0xb0b80e80, 0, "sceNetAdhocctlCreateEnterGameModeMin"}, // ??
|
|
};
|
|
|
|
const HLEFunction sceNetAdhocDiscover[] = {
|
|
{0x941B3877, 0, "sceNetAdhocDiscoverInitStart"},
|
|
{0x52DE1B97, 0, "sceNetAdhocDiscoverUpdate"},
|
|
{0x944DDBC6, 0, "sceNetAdhocDiscoverGetStatus"},
|
|
{0xA2246614, 0, "sceNetAdhocDiscoverTerm"},
|
|
{0xF7D13214, 0, "sceNetAdhocDiscoverStop"},
|
|
{0xA423A21B, 0, "sceNetAdhocDiscoverRequestSuspend"},
|
|
};
|
|
|
|
void Register_sceNetAdhoc() {
|
|
RegisterModule("sceNetAdhoc", ARRAY_SIZE(sceNetAdhoc), sceNetAdhoc);
|
|
RegisterModule("sceNetAdhocMatching", ARRAY_SIZE(sceNetAdhocMatching), sceNetAdhocMatching);
|
|
RegisterModule("sceNetAdhocDiscover", ARRAY_SIZE(sceNetAdhocDiscover), sceNetAdhocDiscover);
|
|
RegisterModule("sceNetAdhocctl", ARRAY_SIZE(sceNetAdhocctl), sceNetAdhocctl);
|
|
}
|