ppsspp/Core/HLE/proAdhoc.cpp
Henrik Rydgard 60ed75f68a Add option to disable networking, tries to replicate the old error codes.
Please test if disabling this option "fixes" any games that broke with the adhoc merge.
2013-12-04 11:07:52 +01:00

711 lines
17 KiB
C++

// TODO: Add license
#include "util/text/parsers.h"
#include "proAdhoc.h"
uint32_t fakePoolSize = 0;
SceNetAdhocMatchingContext * contexts = NULL;
int one = 1;
bool friendFinderRunning = false;
SceNetAdhocctlPeerInfo * friends = NULL;
SceNetAdhocctlScanInfo * networks = NULL;
int eventHandlerUpdate = -1;
int threadStatus = ADHOCCTL_STATE_DISCONNECTED;
int metasocket;
SceNetAdhocctlParameter parameter;
std::thread friendFinderThread;
recursive_mutex peerlock;
SceNetAdhocPdpStat * pdp[255];
SceNetAdhocPtpStat * ptp[255];
int isLocalMAC(const SceNetEtherAddr * addr) {
SceNetEtherAddr saddr;
getLocalMac(&saddr);
// Compare MAC Addresses
int match = memcmp((const void *)addr, (const void *)&saddr, ETHER_ADDR_LEN);
// Return Result
return (match == 0);
}
int isPDPPortInUse(uint16_t port) {
// Iterate Elements
int i = 0; for(; i < 255; i++) if(pdp[i] != NULL && pdp[i]->lport == port) return 1;
// Unused Port
return 0;
}
int isPTPPortInUse(uint16_t port) {
// Iterate Sockets
int i = 0; for(; i < 255; i++) if(ptp[i] != NULL && ptp[i]->lport == port) return 1;
// Unused Port
return 0;
}
void addFriend(SceNetAdhocctlConnectPacketS2C * packet) {
// Allocate Structure
SceNetAdhocctlPeerInfo * peer = (SceNetAdhocctlPeerInfo *)malloc(sizeof(SceNetAdhocctlPeerInfo));
// Allocated Structure
if(peer != NULL) {
// Clear Memory
memset(peer, 0, sizeof(SceNetAdhocctlPeerInfo));
// Link to existing Peers
peer->next = friends;
// Save Nickname
peer->nickname = packet->name;
// Save MAC Address
peer->mac_addr = packet->mac;
// Save IP Address
peer->ip_addr = packet->ip;
// Multithreading Lock
peerlock.lock();
// Link into Peerlist
friends = peer;
// Multithreading Unlock
peerlock.unlock();
}
}
void changeBlockingMode(int fd, int nonblocking) {
unsigned long on = 1;
unsigned long off = 0;
#ifdef _MSC_VER
if(nonblocking){
// Change to Non-Blocking Mode
ioctlsocket(fd,FIONBIO,&on);
}else {
// Change to Blocking Mode
ioctlsocket(fd, FIONBIO, &off);
}
#else
if(nonblocking == 1) fcntl(fd, F_SETFL, O_NONBLOCK);
else {
// Get Flags
int flags = fcntl(fd, F_GETFL);
// Remove Non-Blocking Flag
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
}
#endif
}
int countAvailableNetworks(void) {
// Network Count
int count = 0;
// Group Reference
SceNetAdhocctlScanInfo * group = networks;
// Count Groups
for(; group != NULL; group = group->next) count++;
// Return Network Count
return count;
}
void deleteAllPDP(void) {
// Iterate Element
int i = 0; for(; i < 255; i++) {
// Active Socket
if(pdp[i] != NULL) {
// Close Socket
closesocket(pdp[i]->id);
// Free Memory
free(pdp[i]);
// Delete Reference
pdp[i] = NULL;
}
}
}
void deleteAllPTP(void) {
// Iterate Element
int i = 0; for(; i < 255; i++) {
// Active Socket
if(ptp[i] != NULL) {
// Close Socket
closesocket(ptp[i]->id);
// Free Memory
free(ptp[i]);
// Delete Reference
ptp[i] = NULL;
}
}
}
void deleteFriendByIP(uint32_t ip) {
// Previous Peer Reference
SceNetAdhocctlPeerInfo * prev = NULL;
// Peer Pointer
SceNetAdhocctlPeerInfo * peer = friends;
// Iterate Peers
for(; peer != NULL; peer = peer->next) {
// Found Peer
if(peer->ip_addr == ip) {
// Multithreading Lock
peerlock.lock();
// Unlink Left (Beginning)
if(prev == NULL)friends = peer->next;
// Unlink Left (Other)
else prev->next = peer->next;
// Multithreading Unlock
peerlock.unlock();
// Free Memory
free(peer);
// Stop Search
break;
}
// Set Previous Reference
prev = peer;
}
}
int findFreeMatchingID(void) {
// Minimum Matching ID
int min = 1;
// Maximum Matching ID
int max = 0;
// Find highest Matching ID
SceNetAdhocMatchingContext * item = contexts; for(; item != NULL; item = item->next) {
// New Maximum
if(max < item->id) max = item->id;
}
// Find unoccupied ID
int i = min; for(; i < max; i++) {
// Found unoccupied ID
if(findMatchingContext(i) == NULL) return i;
}
// Append at virtual end
return max + 1;
}
SceNetAdhocMatchingContext * findMatchingContext(int id) {
// Iterate Matching Context List
SceNetAdhocMatchingContext * item = contexts; for(; item != NULL; item = item->next) { // Found Matching ID
if(item->id == id) return item;
}
// Context not found
return NULL;
}
void freeFriendsRecursive(SceNetAdhocctlPeerInfo * node) {
// End of List
if(node == NULL) return;
// Increase Recursion Depth
freeFriendsRecursive(node->next);
// Free Memory
free(node);
}
int friendFinder(){
// Receive Buffer
int rxpos = 0;
uint8_t rx[1024];
// Chat Packet
SceNetAdhocctlChatPacketC2S chat;
chat.base.opcode = OPCODE_CHAT;
// Last Ping Time
uint64_t lastping = 0;
// Last Time Reception got updated
uint64_t lastreceptionupdate = 0;
uint64_t now;
// Finder Loop
while(friendFinderRunning) {
// Acquire Network Lock
//_acquireNetworkLock();
// Ping Server
now = real_time_now()*1000.0;
if(now - lastping >= 100) {
// Update Ping Time
lastping = now;
// Prepare Packet
uint8_t opcode = OPCODE_PING;
// Send Ping to Server
send(metasocket, (const char *)&opcode, 1,0);
}
// Send Chat Messages
//while(popFromOutbox(chat.message))
//{
// // Send Chat to Server
// sceNetInetSend(metasocket, (const char *)&chat, sizeof(chat), 0);
//}
// Wait for Incoming Data
int received = recv(metasocket, (char *)(rx + rxpos), sizeof(rx) - rxpos,0);
// Free Network Lock
//_freeNetworkLock();
// Received Data
if(received > 0) {
// Fix Position
rxpos += received;
// Log Incoming Traffic
//printf("Received %d Bytes of Data from Server\n", received);
INFO_LOG(SCENET, "Received %d Bytes of Data from Adhoc Server", received);
}
// Handle Packets
if(rxpos > 0) {
// BSSID Packet
if(rx[0] == OPCODE_CONNECT_BSSID) {
// Enough Data available
if(rxpos >= (int)sizeof(SceNetAdhocctlConnectBSSIDPacketS2C)) {
// Cast Packet
SceNetAdhocctlConnectBSSIDPacketS2C * packet = (SceNetAdhocctlConnectBSSIDPacketS2C *)rx;
// Update BSSID
parameter.bssid.mac_addr = packet->mac;
// Change State
threadStatus = ADHOCCTL_STATE_CONNECTED;
// Notify Event Handlers
CoreTiming::ScheduleEvent_Threadsafe_Immediate(eventHandlerUpdate, join32(ADHOCCTL_EVENT_CONNECT, 0));
// Move RX Buffer
memmove(rx, rx + sizeof(SceNetAdhocctlConnectBSSIDPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlConnectBSSIDPacketS2C));
// Fix RX Buffer Length
rxpos -= sizeof(SceNetAdhocctlConnectBSSIDPacketS2C);
}
}
// Chat Packet
else if(rx[0] == OPCODE_CHAT) {
// Enough Data available
if(rxpos >= (int)sizeof(SceNetAdhocctlChatPacketS2C)) {
// Cast Packet
SceNetAdhocctlChatPacketS2C * packet = (SceNetAdhocctlChatPacketS2C *)rx;
// Fix for Idiots that try to troll the "ME" Nametag
if(strcasecmp((char *)packet->name.data, "ME") == 0) strcpy((char *)packet->name.data, "NOT ME");
// Add Incoming Chat to HUD
//printf("Receive chat message %s", packet->base.message);
// Move RX Buffer
memmove(rx, rx + sizeof(SceNetAdhocctlChatPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlChatPacketS2C));
// Fix RX Buffer Length
rxpos -= sizeof(SceNetAdhocctlChatPacketS2C);
}
}
// Connect Packet
else if(rx[0] == OPCODE_CONNECT) {
// Enough Data available
if(rxpos >= (int)sizeof(SceNetAdhocctlConnectPacketS2C)) {
// Log Incoming Peer
INFO_LOG(SCENET,"Incoming Peer Data...");
// Cast Packet
SceNetAdhocctlConnectPacketS2C * packet = (SceNetAdhocctlConnectPacketS2C *)rx;
// Add User
addFriend(packet);
// Update HUD User Count
#ifdef LOCALHOST_AS_PEER
setUserCount(getActivePeerCount());
#else
// setUserCount(getActivePeerCount()+1);
#endif
// Move RX Buffer
memmove(rx, rx + sizeof(SceNetAdhocctlConnectPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlConnectPacketS2C));
// Fix RX Buffer Length
rxpos -= sizeof(SceNetAdhocctlConnectPacketS2C);
}
}
// Disconnect Packet
else if(rx[0] == OPCODE_DISCONNECT) {
// Enough Data available
if(rxpos >= (int)sizeof(SceNetAdhocctlDisconnectPacketS2C)) {
// Log Incoming Peer Delete Request
INFO_LOG(SCENET,"FriendFinder: Incoming Peer Data Delete Request...");
// Cast Packet
SceNetAdhocctlDisconnectPacketS2C * packet = (SceNetAdhocctlDisconnectPacketS2C *)rx;
// Delete User by IP
deleteFriendByIP(packet->ip);
// Update HUD User Count
#ifdef LOCALHOST_AS_PEER
setUserCount(_getActivePeerCount());
#else
//setUserCount(_getActivePeerCount()+1);
#endif
// Move RX Buffer
memmove(rx, rx + sizeof(SceNetAdhocctlDisconnectPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlDisconnectPacketS2C));
// Fix RX Buffer Length
rxpos -= sizeof(SceNetAdhocctlDisconnectPacketS2C);
}
}
// Scan Packet
else if(rx[0] == OPCODE_SCAN) {
// Enough Data available
if(rxpos >= (int)sizeof(SceNetAdhocctlScanPacketS2C)) {
// Log Incoming Network Information
INFO_LOG(SCENET,"Incoming Group Information...");
// Cast Packet
SceNetAdhocctlScanPacketS2C * packet = (SceNetAdhocctlScanPacketS2C *)rx;
// Allocate Structure Data
SceNetAdhocctlScanInfo * group = (SceNetAdhocctlScanInfo *)malloc(sizeof(SceNetAdhocctlScanInfo));
// Allocated Structure Data
if(group != NULL)
{
// Clear Memory
memset(group, 0, sizeof(SceNetAdhocctlScanInfo));
// Link to existing Groups
group->next = networks;
// Copy Group Name
group->group_name = packet->group;
// Set Group Host
group->bssid.mac_addr = packet->mac;
// Link into Group List
networks = group;
}
// Move RX Buffer
memmove(rx, rx + sizeof(SceNetAdhocctlScanPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlScanPacketS2C));
// Fix RX Buffer Length
rxpos -= sizeof(SceNetAdhocctlScanPacketS2C);
}
}
// Scan Complete Packet
else if(rx[0] == OPCODE_SCAN_COMPLETE) {
// Log Scan Completion
INFO_LOG(SCENET,"FriendFinder: Incoming Scan complete response...");
// Change State
threadStatus = ADHOCCTL_STATE_DISCONNECTED;
// Notify Event Handlers
CoreTiming::ScheduleEvent_Threadsafe_Immediate(eventHandlerUpdate,join32(ADHOCCTL_EVENT_SCAN, 0));
//int i = 0; for(; i < ADHOCCTL_MAX_HANDLER; i++)
//{
// // Active Handler
// if(_event_handler[i] != NULL) _event_handler[i](ADHOCCTL_EVENT_SCAN, 0, _event_args[i]);
//}
// Move RX Buffer
memmove(rx, rx + 1, sizeof(rx) - 1);
// Fix RX Buffer Length
rxpos -= 1;
}
}
// Original value was 10 ms, I think 100 is just fine
sleep_ms(100);
}
// Log Shutdown
INFO_LOG(SCENET, "FriendFinder: End of Friend Finder Thread");
// Return Success
return 0;
}
int getActivePeerCount(void) {
// Counter
int count = 0;
// #ifdef LOCALHOST_AS_PEER
// // Increase for Localhost
// count++;
// #endif
// Peer Reference
SceNetAdhocctlPeerInfo * peer = friends;
// Iterate Peers
for(; peer != NULL; peer = peer->next) {
// Increase Counter
count++;
}
// Return Result
return count;
}
int getLocalIp(sockaddr_in * SocketAddress){
#ifdef _MSC_VER
// Get local host name
char szHostName[128] = "";
if(::gethostname(szHostName, sizeof(szHostName))) {
// Error handling
}
// Get local IP addresses
struct hostent *pHost = 0;
pHost = ::gethostbyname(szHostName);
if(pHost) {
memcpy(&SocketAddress->sin_addr, pHost->h_addr_list[0], pHost->h_length);
return 0;
}
return -1;
#else
SocketAddress->sin_addr.s_addr = inet_addr("192.168.12.1");
return 0;
#endif
}
void getLocalMac(SceNetEtherAddr * addr){
// Read MAC Address from config
uint8_t mac[ETHER_ADDR_LEN] = {0};
if (!ParseMacAddress(g_Config.localMacAddress.c_str(), mac)) {
ERROR_LOG(SCENET, "Error parsing mac address %s", g_Config.localMacAddress.c_str());
}
memcpy(addr, mac, ETHER_ADDR_LEN);
}
int getPTPSocketCount(void) {
// Socket Counter
int counter = 0;
// Count Sockets
int i = 0; for(; i < 255; i++) if(ptp[i] != NULL) counter++;
// Return Socket Count
return counter;
}
int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){
int iResult = 0;
#ifdef _MSC_VER
WSADATA data;
iResult = WSAStartup(MAKEWORD(2,2),&data);
if(iResult != NOERROR){
ERROR_LOG(SCENET, "Wsa failed");
return iResult;
}
#endif
metasocket = (int)INVALID_SOCKET;
metasocket = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP);
if(metasocket == INVALID_SOCKET){
ERROR_LOG(SCENET,"invalid socket");
return -1;
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(27312); // Maybe read this from config too
// Resolve dns
addrinfo * resultAddr;
addrinfo * ptr;
in_addr serverIp;
iResult = getaddrinfo(g_Config.proAdhocServer.c_str(),0,NULL,&resultAddr);
if(iResult != 0){
ERROR_LOG(SCENET, "Dns error\n");
return iResult;
}
for(ptr = resultAddr; ptr != NULL; ptr = ptr->ai_next){
switch(ptr->ai_family){
case AF_INET:
serverIp = ((sockaddr_in *)ptr->ai_addr)->sin_addr;
}
}
server_addr.sin_addr = serverIp;
iResult = connect(metasocket,(sockaddr *)&server_addr,sizeof(server_addr));
if(iResult == SOCKET_ERROR){
ERROR_LOG(SCENET,"Socket error");
return iResult;
}
memset(&parameter,0,sizeof(parameter));
strcpy((char *)&parameter.nickname.data, g_Config.sNickName.c_str());
parameter.channel = 1; // Fake Channel 1
// Prepare Login Packet
getLocalMac(&parameter.bssid.mac_addr);
SceNetAdhocctlLoginPacketC2S packet;
packet.base.opcode = OPCODE_LOGIN;
SceNetEtherAddr addres;
getLocalMac(&addres);
packet.mac = addres;
strcpy((char *)packet.name.data, g_Config.sNickName.c_str());
memcpy(packet.game.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN);
int sent = send(metasocket, (char*)&packet, sizeof(packet), 0);
changeBlockingMode(metasocket,1); // Change to non-blocking
if(sent > 0){
return 0;
}else{
return -1;
}
}
int isBroadcastMAC(const SceNetEtherAddr * addr) {
// Broadcast MAC
if(memcmp(addr->data, "\xFF\xFF\xFF\xFF\xFF\xFF", ETHER_ADDR_LEN) == 0) return 1;
// Normal MAC
return 0;
}
int resolveIP(uint32_t ip, SceNetEtherAddr * mac) {
sockaddr_in addr;
getLocalIp(&addr);
uint32 localIp = addr.sin_addr.s_addr;
if(ip == localIp){
getLocalMac(mac);
return 0;
}
// Multithreading Lock
peerlock.lock();
// Peer Reference
SceNetAdhocctlPeerInfo * peer = friends;
// Iterate Peers
for(; peer != NULL; peer = peer->next) {
// Found Matching Peer
if(peer->ip_addr == ip) {
// Copy Data
*mac = peer->mac_addr;
// Multithreading Unlock
peerlock.unlock();
// Return Success
return 0;
}
}
// Multithreading Unlock
peerlock.unlock();
// Peer not found
return -1;
}
int resolveMAC(SceNetEtherAddr * mac, uint32_t * ip) {
// Get Local MAC Address
SceNetEtherAddr localMac;
getLocalMac(&localMac);
// Local MAC Requested
if(memcmp(&localMac, mac, sizeof(SceNetEtherAddr)) == 0) {
// Get Local IP Address
sockaddr_in sockAddr;
getLocalIp(&sockAddr);
*ip = sockAddr.sin_addr.s_addr;
return 0; // return succes
}
// Multithreading Lock
peerlock.lock();
// Peer Reference
SceNetAdhocctlPeerInfo * peer = friends;
// Iterate Peers
for(; peer != NULL; peer = peer->next) {
// Found Matching Peer
if(memcmp(&peer->mac_addr, mac, sizeof(SceNetEtherAddr)) == 0) {
// Copy Data
*ip = peer->ip_addr;
// Multithreading Unlock
peerlock.unlock();
// Return Success
return 0;
}
}
// Multithreading Unlock
peerlock.unlock();
// Peer not found
return -1;
}
int validNetworkName(const SceNetAdhocctlGroupName * group_name) {
// Result
int valid = 1;
// Name given
if(group_name != NULL) {
// Iterate Name Characters
int i = 0; for(; i < ADHOCCTL_GROUPNAME_LEN && valid; i++) {
// End of Name
if(group_name->data[i] == 0) break;
// Not a digit
if(group_name->data[i] < '0' || group_name->data[i] > '9') {
// Not 'A' to 'Z'
if(group_name->data[i] < 'A' || group_name->data[i] > 'Z') {
// Not 'a' to 'z'
if(group_name->data[i] < 'a' || group_name->data[i] > 'z') {
// Invalid Name
valid = 0;
}
}
}
}
}
// Return Result
return valid;
}
u64 join32(u32 num1, u32 num2){
return (u64)num2 << 32 | num1;
}
void split64(u64 num, int buff[]){
int num1 = (int)(num&firstMask);
int num2 = (int)((num&secondMask)>>32);
buff[0] = num1;
buff[1] = num2;
}