// 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/.

#include <mutex>
// 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"

// 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;

std::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) {
	std::lock_guard<std::mutex> 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) {
	std::lock_guard<std::mutex> adhocGuard(adhocEvtMtx);
	matchingEvents.push_back(ArgsPtr);
}

static int getBlockingFlag(int id) {
#ifdef _WIN32
	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, 0, false);
		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,&parameter);
			// Return Success
			return 0;
		}

		// Invalid Arguments
		return ERROR_NET_ADHOCCTL_INVALID_ARG;
	}

	// Library uninitialized
	return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
}

/**
 * Adhoc Emulator PDP Send Call
 * @param id Socket File Descriptor
 * @param daddr Target MAC Address
 * @param dport Target Port
 * @param data Data Payload
 * @param len Payload Length
 * @param timeout Send Timeout
 * @param flag Nonblocking Flag
 * @return 0 on success or... ADHOC_INVALID_ARG, ADHOC_NOT_INITIALIZED, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_INVALID_ADDR, ADHOC_INVALID_PORT, ADHOC_INVALID_DATALEN, ADHOC_SOCKET_ALERTED, ADHOC_TIMEOUT, ADHOC_THREAD_ABORTED, ADHOC_WOULD_BLOCK, NET_NO_SPACE, NET_INTERNAL
 */
static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int len, int timeout, int flag) {
	DEBUG_LOG(SCENET, "sceNetAdhocPdpSend(%i, %s, %i, %p, %i, %i, %i)", id, mac, port, data, len, timeout, flag);
	if (!g_Config.bEnableWlan) {
		return -1;
	}
	SceNetEtherAddr * daddr = (SceNetEtherAddr *)mac;
	uint16_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(&parameter.group_name, 0, sizeof(parameter.group_name));

			// Set Disconnected State
			threadStatus = ADHOCCTL_STATE_DISCONNECTED;

			// Set HUD Connection Status
			//setConnectionStatus(0);

			// Prepare Packet
			uint8_t opcode = OPCODE_DISCONNECT;

			// Acquire Network Lock
			//_acquireNetworkLock();

			// Send Disconnect Request Packet
			int iResult = send(metasocket, (const char *)&opcode, 1, 0);
			if (iResult == SOCKET_ERROR) {
				ERROR_LOG(SCENET, "Socket error (%i) when sending", errno);
			}

			// Free Network Lock
			//_freeNetworkLock();

			// Multithreading Lock
			//peerlock.lock();

			// Clear Peer List
			freeFriendsRecursive(friends);
			INFO_LOG(SCENET, "Cleared Peer List.");

			// 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)
		{
			std::lock_guard<std::mutex> 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(&parameter.group_name, 0, sizeof(parameter.group_name));

				// Prepare Connect Packet
				SceNetAdhocctlConnectPacketC2S packet;

				// Clear Packet Memory
				memset(&packet, 0, sizeof(packet));

				// Set Packet Opcode
				packet.base.opcode = OPCODE_CONNECT;

				// Set Target Group
				if (groupNameStruct != NULL) packet.group = *groupNameStruct;

				// Acquire Network Lock

				// Send Packet
				int iResult = send(metasocket, (const char *)&packet, sizeof(packet), 0);
				if (iResult == SOCKET_ERROR) {
					ERROR_LOG(SCENET, "Socket error (%i) when sending", errno);
					//return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // ERROR_NET_ADHOCCTL_DISCONNECTED; // ERROR_NET_ADHOCCTL_BUSY;
					//Faking success, to prevent Full Auto 2 from freezing while Initializing Network
					threadStatus = ADHOCCTL_STATE_CONNECTED;
					// Notify Event Handlers, Needed for the Nickname to be shown on the screen when success is faked
					// Might be better not to notify the game when faking success (failed to connect to adhoc server), at least the player will know that it failed to connect 
					//__UpdateAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); //CoreTiming::ScheduleEvent_Threadsafe_Immediate(eventAdhocctlHandlerUpdate, join32(ADHOCCTL_EVENT_CONNECT, 0)); 
				}

				// Free Network Lock

				// Set HUD Connection Status
				//setConnectionStatus(1);

				// Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session
				if (friendFinderRunning) {
					int cnt = 0;
					while ((threadStatus != ADHOCCTL_STATE_CONNECTED) && (cnt < 5000)) {
						sleep_ms(1);
						cnt++;
					}
				}

				// Return Success
				return 0;
			}
			
			// Connected State
			return ERROR_NET_ADHOCCTL_BUSY; // ERROR_NET_ADHOCCTL_BUSY may trigger the game (ie. Ford Street Racing) to call sceNetAdhocctlDisconnect
		}
		
		// Invalid Argument
		return ERROR_NET_ADHOC_INVALID_ARG;
	}
	// Library uninitialized
	return ERROR_NET_ADHOCCTL_NOT_INITIALIZED;
}

static int sceNetAdhocctlConnect(u32 ptrToGroupName) {
	if (Memory::IsValidAddress(ptrToGroupName)) {
		INFO_LOG(SCENET, "sceNetAdhocctlConnect(groupName=%s) at %08x", Memory::GetCharPointer(ptrToGroupName), currentMIPS->pc);
		return sceNetAdhocctlCreate(Memory::GetCharPointer(ptrToGroupName));
	} 
		
	return ERROR_NET_ADHOC_INVALID_ARG; // ERROR_NET_ADHOC_INVALID_ADDR;
}

static int sceNetAdhocctlCreateEnterGameMode(const char *groupName, int unknown, int playerNum, u32 macsAddr, int timeout, int unknown2) {
	ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlCreateEnterGameMode(%s, %i, %i, %08x, %i, %i) at %08x", groupName, unknown, playerNum, macsAddr, timeout, unknown2, currentMIPS->pc);
	return -1;
}

static int sceNetAdhocctlJoinEnterGameMode(const char *groupName, const char *macAddr, int timeout, int unknown2) {
	ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlJoinEnterGameMode(%s, %s, %i, %i) at %08x", groupName, macAddr, timeout, unknown2, currentMIPS->pc);
	return -1;
}

/**
* Create and Join a GameMode Network as Host (with Minimum Peer Check)
* @param group_name Virtual Network Name
* @param game_type Network Type (1A, 1B, 2A)
* @param min_members Minimum Number of Peers
* @param num_members Total Number of Peers (including Host)
* @param members MAC Address List of Peers (own MAC at Index 0)
* @param timeout Timeout Value (in Microseconds)
* @param flag Unused Bitflags
* @return 0 on success or... ADHOCCTL_NOT_INITIALIZED, ADHOCCTL_INVALID_ARG, ADHOCCTL_BUSY, ADHOCCTL_CHANNEL_NOT_AVAILABLE
*/
int sceNetAdhocctlCreateEnterGameModeMin(const char *group_name, int game_type, int min_members, int num_members, u32 membersAddr, u32 timeout, int flag)
{
	ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlCreateEnterGameModeMin(%s, %i, %i, %i, %08x, %d, %i) at %08x", group_name, game_type, min_members, num_members, membersAddr, timeout, flag, currentMIPS->pc);
	// We don't really need the Minimum User Check
	return sceNetAdhocctlCreateEnterGameMode(group_name, game_type, num_members, membersAddr, timeout, flag);
}

int sceNetAdhocTerm() {
	INFO_LOG(SCENET, "sceNetAdhocTerm()");
	// WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup all the sockets right?

	if (netAdhocctlInited) sceNetAdhocctlTerm();

	// Library is initialized
	if (netAdhocInited) {
		// Delete fake PSP Thread
		if (threadAdhocID != 0) {
			__KernelStopThread(threadAdhocID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocThread stopped");
			__KernelDeleteThread(threadAdhocID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocThread deleted");
		}

		// Delete PDP Sockets
		deleteAllPDP();

		// Delete PTP Sockets
		deleteAllPTP();

		// Delete Gamemode Buffer
		//deleteAllGMB();

		// Terminate Internet Library
		//sceNetInetTerm();

		// 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*/) {
				//sport 0 should be shifted back to 0 when using offset Phantasy Star Portable 2 use this
				if (sport == 0) sport = -(int)portOffset;
				// 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 std::recursive_mutex;
								context->eventlock = new std::recursive_mutex; 
								context->inputlock = new std::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()
{
	{
		std::lock_guard<std::mutex> adhocGuard(adhocEvtMtx);

		for (auto &params : 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 &param : 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;
}
