CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/HLE/proAdhocServer.cpp
Views: 1401
// Copyright (c) 2014- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.161718// proAdhocServer1920// This is a direct port of Coldbird's code from http://code.google.com/p/aemu/21// All credit goes to him!2223#include "ppsspp_config.h"2425#include <cstdlib>26#include <cstdio>27#include <cstring>28#include <signal.h>2930#if defined(HAVE_LIBNX) || PPSSPP_PLATFORM(SWITCH)31#include <netdb.h>32#include <switch.h>33// Missing include, *shrugs*34extern "C" struct hostent *gethostbyname(const char *name);35#endif // defined(HAVE_LIBNX) || PPSSPP_PLATFORM(SWITCH)3637#include <sys/types.h>38// Net stuff39#if defined(_WIN32)40#include <WinSock2.h>41#include <WS2tcpip.h>42#else43#include <sys/socket.h>44#include <netinet/in.h>45#include <netinet/tcp.h>46#endif4748#include <fcntl.h>49#include <errno.h>50//#include <sqlite3.h>5152#ifndef MSG_NOSIGNAL53// Default value to 0x00 (do nothing) in systems where it's not supported.54#define MSG_NOSIGNAL 0x0055#endif5657#include "Common/Data/Text/I18n.h"58#include "Common/Thread/ThreadUtil.h"59#include "Common/System/OSD.h"6061#include "Common/File/FileUtil.h"62#include "Common/TimeUtil.h"63#include "Core/Util/PortManager.h"64#include "Core/Instance.h"65#include "Core/Core.h"66#include "Core/HLE/proAdhocServer.h"6768// User Count69uint32_t _db_user_count = 0;7071// User Database72SceNetAdhocctlUserNode * _db_user = NULL;7374// Game Database75SceNetAdhocctlGameNode * _db_game = NULL;7677// Server Status78std::atomic<bool> adhocServerRunning(false);79std::thread adhocServerThread;8081// Crosslink database for cross region Adhoc play82std::vector<db_crosslink> crosslinks;83static const db_crosslink default_crosslinks[] = {84// Ace Combat X2 - Joint Assault85{ "ULES01408", "ULUS10511" },86{ "NPJH50263", "ULUS10511" },8788// Armored Core 3 Portable89{ "ULJM05492", "NPUH10023" },9091// BlazBlue - Continuum Shift 292{ "NPJH50401", "ULUS10579" },9394// Blood Bowl95{ "ULES01230", "ULUS10516" },9697// Bomberman98{ "ULJM05034", "ULUS10121" },99{ "ULES00469", "ULUS10121" },100{ "ULJM05316", "ULUS10121" },101102// Bomberman Land103{ "ULJM05181", "ULUS10319" },104{ "ULJM05319", "ULUS10319" },105{ "ULES00959", "ULUS10319" },106107// Call of Duty - Roads to Victory108{ "ULES00643", "ULUS10218" },109110// Dissidia 012 Duodecim Final Fantasy111{ "ULES01505", "ULUS10566" },112{ "NPJH50377", "ULUS10566" },113114// Dissidia Final Fantasy115{ "ULES01270", "ULUS10437" },116{ "ULJM05262", "ULUS10437" },117118// Dragon Ball Z - Shin Budokai119{ "ULJS00049", "ULUS10081" },120{ "ULKS46085", "ULUS10081" },121{ "ULES00309", "ULUS10081" },122123// Dragon Ball Z - Shin Budokai 2124{ "ULJS00107", "ULUS10234" },125{ "ULES00789", "ULUS10234" },126127// Dragon Ball Z - Tenkaichi Tag Team128{ "ULES01456", "ULUS10537" },129130// Dungeon Siege - Throne of Agony131{ "ULES00569", "ULUS10177" },132133// Everybody's Tennis134{ "UCJS10101", "UCUS98701" },135{ "UCES01420", "UCUS98701" },136137// Fat Princess - Fistful of Cake138{ "UCES01312", "UCUS98740" },139{ "NPHG00025", "UCUS98740" },140141// God Eater Burst142{ "ULES01519", "ULUS10563" },143{ "NPJH50352", "ULUS10563" },144145// Gran Turismo146{ "UCES01245", "UCUS98632" },147{ "UCES00543", "UCUS98645" },148149// Gundam VS Gundam - Next Plus150{ "ULJS00250", "NPJH50107" },151{ "ULJS19048", "NPJH50107" },152153// Hatsune Miku - Project Diva Extend154{ "NPJH50465", "ULJM05933" },155156// Hot Pixel157{ "ULES00642", "ULUS10298" },158159// Lord of Arcana160{ "ULJM05767", "ULES01507" },161{ "ULUS10479", "ULES01507" },162163// M.A.C.H. - Modified Air Combat Heroes164{ "ULES00565", "ULUS10180" },165{ "ULES00566", "ULUS10180" },166{ "ULJM05202", "ULUS10180" },167168// Metal Gear Solid - Peace Walker169{ "ULES01372", "NPJH50045" },170{ "ULUS10509", "NPJH50045" },171172// Metal Gear Solid - Portable Ops173{ "ULES00645", "ULUS10202" },174{ "ULJM05193", "ULUS10202" },175176// Metal Gear Solid - Portable Ops +177{ "ULES01003", "ULUS10290" },178{ "ULJM05261", "ULUS10290" },179180// Midnight Club - LA Remix181{ "ULES01144", "ULUS10383" },182{ "ULJS00180", "ULUS10383" },183184// Mod Nation Racers185{ "UCES01327", "UCUS98741" },186{ "UCJS10112", "UCUS98741" },187{ "UCAS40306", "UCUS98741" },188189// Monster Hunter Freedom190{ "ULJM05066", "ULUS10084" },191{ "ULES00318", "ULUS10084" },192193// Monster Hunter Freedom 2194{ "ULJM05156", "ULUS10266" },195{ "ULES00851", "ULUS10266" },196197// Monster Hunter Freedom Unite198{ "ULES01213", "ULUS10391" },199{ "ULJM05500", "ULUS10391" },200201// N+202{ "ULES01026", "ULUS10340" },203204// Need for Speed - Undercover205{ "ULJM05403", "ULUS10376" },206{ "ULJM05612", "ULUS10376" },207{ "ULES01145", "ULUS10376" },208209// Outrun 2006 - Coast 2 Coast210{ "ULES00262", "ULUS10064" },211212// Pangya! - Fantasy Golf213{ "ULJM05440", "ULUS10438" },214{ "ULKS46164", "ULUS10438" },215216// PRO Evolution Soccer 2012217{ "ULES01540", "ULUS10586" },218{ "ULES01541", "ULUS10586" },219{ "ULES01542", "ULUS10586" },220{ "ULAS42289", "ULUS10586" },221222// Patapon 2223{ "UCJS10089", "UCUS98732" },224{ "PSPJ30000", "UCUS98732" },225{ "UCES01177", "UCUS98732" },226{ "UCJS18036", "UCUS98732" },227228// Patapon 3229{ "UCES01421", "UCUS98751" },230{ "NPJG00122", "UCUS98751" },231232// Phantasy Star Portable233{ "ULJM05309", "ULUS10410" },234{ "ULES01218", "ULUS10410" },235{ "ULJM08023", "ULUS10410" },236237// Phantasy Star Portable 2238{ "ULJM05493", "ULUS10529" },239{ "ULJM08030", "ULUS10529" },240{ "ULES01439", "ULUS10529" },241242// Resistance - Retribution243{ "UCES01184", "UCJS10090" },244{ "UCUS98668", "UCJS10090" },245246// Rocky Balboa247{ "ULUS10233", "ULES00670" },248249// SOCOM - Fireteam Bravo250{ "UCES00038", "UCUS98615" },251{ "UCJS10102", "UCUS98615" },252253// SOCOM - Fireteam Bravo 3254{ "UCES01242", "UCUS98716" },255{ "NPJG00035", "UCUS98716" },256257// Shrek - Smash and Crash Racing258{ "ULES00618", "ULUS10194" },259260// Smash Court Tennis 3261{ "ULJS00098", "UCES00758" },262{ "ULUS10269", "UCES00758" },263264// Soul Calibur - Broken Destiny265{ "ULES01298", "ULUS10457" },266{ "ULJS00202", "ULUS10457" },267268// Split Second - Velocity269{ "ULES01402", "ULUS10513" },270{ "ULJM05812", "ULUS10513" },271272// Street Fighter Alpha 3 MAX273{ "ULJM05082", "ULUS10062" },274{ "ULES00235", "ULUS10062" },275{ "ULJM05225", "ULUS10062" },276277// Taiko no Tatsujin Portable DX"278{ "ULJS00383", "NPJH50426" },279280// Tekken 6281{ "ULES01376", "ULUS10466" },282{ "NPJH50184", "ULUS10466" },283{ "ULJS00224", "ULUS10466" },284285// TRON - Evolution286{ "ULES01495", "ULUS10548" },287288// Untold Legends - Brotherhood of the Blade289{ "ULES00046", "ULUS10003" },290{ "ULJM05087", "ULUS10003" },291{ "ULKS46015", "ULUS10003" },292293// Untold Legends - The Warrior's Code294{ "ULES00301", "ULUS10086" },295{ "ULJM05179", "ULUS10086" },296{ "ULKS46069", "ULUS10086" },297298// Virtua Tennis 3299{ "ULES00763", "ULUS10246" },300301// World Series of Poker 2008 - Battle for the Bracelets302{ "ULES00991", "ULUS10321" },303304// Worms Battle Islands305{ "NPEH00019", "NPUH10045" },306307// Worms Open Warfare308{ "ULES00268", "ULUS10065" },309310// Worms Open Warfare 2311{ "ULES00819", "ULUS10260" },312313// Yu-Gi-Oh! 5D's Tag Force 5314{ "ULUS10555", "ULJM05734" },315{ "ULES01474", "ULJM05734" },316};317318std::vector<db_productid> productids;319static const db_productid default_productids[] = {320{ "ULUS10511", "Ace Combat X2 - Joint Assault" },321{ "ULUS10245", "Alien Syndrome" },322{ "NPUH10023", "Armored Core 3 Portable" },323{ "ULES00719", "Asphalt - Urban GT 2" },324{ "ULUS10579", "BlazBlue - Continuum Shift 2" },325{ "ULUS10519", "BlazBlue Calamity Trigger" },326{ "UCJS10110", "Bleach Heat The Soul 7" },327{ "ULUS10516", "Blood Bowl" },328{ "ULUS10121", "Bomberman" },329{ "ULUS10319", "Bomberman Land" },330{ "ULES00703", "Burnout Dominator" },331{ "ULES00125", "Burnout Legends" },332{ "ULJM05538", "Busou Shinki - Battle Masters" },333{ "ULUS10057", "Bust A Move Deluxe" },334{ "ULUS10218", "Call of Duty - Roads to Victory" },335{ "ULUS10351", "Code Lyoko - Quest for Infinity" },336{ "NPJH50583", "Conception - Please have my children!" },337{ "ULUS10044", "Crash Tag Team Racing" },338{ "ULUS10100", "Def Jam Fight For NY - The Takeover" },339{ "NPJH50588", "Digimon World Re:Digitize" },340{ "ULUS10566", "Dissidia 012 Duodecim Final Fantasy" },341{ "ULUS10437", "Dissidia Final Fantasy" },342{ "ULUS10081", "Dragon Ball Z - Shin Budokai" },343{ "ULUS10234", "Dragon Ball Z - Shin Budokai 2" },344{ "ULUS10537", "Dragon Ball Z - Tenkaichi Tag Team" },345//maybe we can crosslinks this 2 region to ULUS10537 not having the game to test346{ "ULJS00311", "Dragon Ball Z - Tenkaichi Tag Team" },347{ "NPJH90135", "Dragon Ball Z - Tenkaichi Tag Team" },348{ "ULJM05127", "Dragon Quest & Final Fantasy in Itadaki Street Special" },349{ "ULES00847", "Dungeon Explorer - Warriors of Ancient Arts" },350{ "ULUS10177", "Dungeon Siege - Throne of Agony" },351{ "ULUS10170", "Dynasty Warrior 2" },352//looks like can be crosslinked too353{ "ULES01221", "Dynasty Warriors - Strike Force" },354{ "ULUS10416", "Dynasty Warriors - Strike Force" },355{ "UCUS98701", "Everybody's Tennis" },356{ "UCUS98740", "Fat Princess - Fistful of Cake" },357{ "ULJM05360", "Fate Tiger Colosseum Upper" },358{ "ULUS10297", "Final Fantasy Tactics - The War of the Lions" },359{ "ULES00850", "Final Fantasy Tactics - War of the Lions" },360{ "NPJH50443", "Final Fantasy Type 0" },361{ "NPJH50468", "Frontier Gate" },362{ "NPJH50721", "Frontier Gate Boost+" },363{ "ULES01432", "Full Metal Alchemist - Brotherhood" },364{ "ULUS10490", "GTA Chinatown Wars" },365{ "ULUS10160", "GTA Vice City Stories" },366{ "ULUS10210", "Ghost Rider" },367{ "ULJS00237", "God Eater" },368{ "NPJH50832", "God Eater 2" },369{ "ULUS10563", "God Eater Burst" },370{ "UCUS98632", "Gran Turismo" },371{ "NPJH50107", "Gundam VS Gundam - Next Plus" },372{ "ULJM05933", "Hatsune Miku - Project Diva Extend" },373{ "ULUS10298", "Hot Pixel" },374{ "ULJM05709", "K-ON! Houkago Live" },375{ "NPJH50221", "Kateikyoushi Hitman Reborn! Kizuna no Tag Battle" },376{ "ULJS00165", "Kidou Senshi Gundam - Gundam vs. Gundam" },377{ "UCUS98646", "Killzone Liberation" },378{ "ULJM05775", "Kingdom Hearts - Birth by Sleep Final Mix" },379{ "ULUS10487", "LEGO Indiana Jones 2" },380{ "NPJH50503", "Lord of Apocalypse" },381{ "ULES01507", "Lord of Arcana" },382{ "ULUS10180", "M.A.C.H. - Modified Air Combat Heroes" },383{ "UCUS98758", "MLB11 - The Show" },384{ "ULUS10581", "Madden NFL 12" },385{ "ULJS00385", "Mahou Shoujo Nanoha A's Portable - The Gears of Destiny" },386{ "ULUS10408", "Mana Khemia Student Alliance" },387{ "ULUS10141", "Medal Of Honor Heroes" },388{ "NPJH50045", "Metal Gear Solid - Peace Walker" },389{ "ULUS10202", "Metal Gear Solid - Portable Ops" },390{ "ULUS10290", "Metal Gear Solid - Portable Ops +" },391{ "ULUS10154", "Metal Slug Anthology" },392{ "ULUS10495", "Metal Slug XX" },393{ "ULES01429", "Metal Slug XX" },394{ "ULES00368", "Micro Machines V4" },395{ "ULUS10383", "Midnight Club - LA Remix" },396{ "UCUS98741", "Mod Nation Racers" },397{ "ULUS10084", "Monster Hunter Freedom" },398{ "ULUS10266", "Monster Hunter Freedom 2" },399{ "ULUS10391", "Monster Hunter Freedom Unite" },400{ "ULJM05800", "Monster Hunter Portable 3rd" },401{ "ULJM06097", "Musou Orochi 2 Special" },402{ "ULUS10340", "N+" },403{ "ULES01578", "NBA 2K13" },404{ "ULUS10598", "NBA 2K13" },405{ "ULUS10349", "Naruto - Ultimate Ninja Heroes 2" },406{ "ULUS10518", "Naruto - Ultimate Ninja Heroes 3" },407{ "ULJS00236", "Naruto - Accel 3" },408{ "ULUS10582", "Naruto Shippuden - Ultimate Ninja Impact" },409{ "ULES01537", "Naruto Shippuden - Ultimate Ninja Impact" },410{ "ULUS10571", "Naruto Shippuden - Kizuna Drive" },411{ "ULES00196", "Need For Speed - Most Wanted" },412{ "ULUS10036", "Need For Speed - Most Wanted" },413{ "ULUS10376", "Need for Speed - Undercover" },414{ "ULKS46004", "Need for Speed - Underground Rivals" },415{ "ULES01340", "Obscure - The Aftermath" },416{ "ULUS10064", "Outrun 2006 - Coast 2 Coast" },417{ "ULUS10586", "PRO Evolution Soccer 2012" },418{ "ULUS10149", "Pac Man - World Rally" },419{ "ULUS10438", "Pangya! - Fantasy Golf" },420{ "UCUS98732", "Patapon 2" },421{ "UCUS98751", "Patapon 3" },422{ "ULUS10410", "Phantasy Star Portable" },423{ "ULUS10529", "Phantasy Star Portable 2" },424//looks like this japan version can crosslink to ULUS10529425{ "NPJH50332", "Phantasy Star Portable 2" },426{ "ULJM05732", "Phantasy Star Portable 2 - Infinity" },427{ "ULES01596", "Pro Evolution Soccer 2014" },428{ "ULES01595", "Pro Evolution Soccer 2015" },429{ "NPJH50520", "Pro Yakyuu Spirits 2012" },430{ "NPJH50838", "Pro Yakyuu Spirits 2014" },431{ "NPJH50492", "Puyo Puyo!! 20th Anniversary" },432{ "ULUS10292", "Renegrade Squadron" },433{ "UCJS10090", "Resistance - Retribution" },434{ "ULES00670", "Rocky Balboa" },435{ "ULJS00360", "Rurouni Kenshin - Meiji Kenkaku Romantan Saisen" },436{ "UCUS98615", "SOCOM - Fireteam Bravo" },437{ "UCUS98645", "SOCOM - Fireteam Bravo 2" },438{ "UCUS98716", "SOCOM - Fireteam Bravo 3" },439{ "NPJH50460", "Sengoku Basara - Chronicles Heroes" },440{ "ULJM05436", "Sengoku Basara - Battle Heroes" },441{ "ULJM05637", "Shin Sangoku Musou - Multi Raid 2" },442{ "ULJM05035", "Shinobido - Tales of the Ninja" },443{ "ULUS10194", "Shrek - Smash and Crash Racing" },444{ "UCES00758", "Smash Court Tennis 3" },445{ "ULUS10195", "Sonic Rivals" },446{ "ULUS10457", "Soul Calibur - Broken Destiny" },447{ "ULUS10513", "Split Second - Velocity" },448{ "ULES00183", "Star Wars Battle Front 2" },449{ "ULUS10062", "Street Fighter Alpha 3 MAX" },450{ "NPUH10020", "Strikers 1945 Plus Portable" },451{ "ULUS10548", "TRON - Evolution" },452{ "NPJH50426", "Taiko no Tatsujin Portable DX" },453{ "ULUS10466", "Tekken 6" },454{ "NPJH50691", "Tokusatsu University" },455//looks like can be crosslinked456{ "ULUS10445", "Tom Clancy's Ghost Recon - Predator" },457{ "ULES01350", "Tom Clancy's Ghost Recon - Predator" },458{ "NPJH50789", "Toukiden" },459{ "NPJH50878", "Toukiden - Kiwami" },460{ "UCUS98601", "Twisted Metal - Head On" },461{ "ULUS10508", "UFC Undisputed 2010" },462{ "ULJS00069", "Ultraman Fighting Evo Zero" },463{ "ULUS10003", "Untold Legends - Brotherhood of the Blade" },464{ "ULUS10086", "Untold Legends - The Warrior's Code" },465{ "ULUS10515", "Valkryia Chronicles 2" },466{ "ULUS10087", "Viewtiful Joe" },467{ "ULUS10246", "Virtua Tennis 3" },468{ "ULUS82741", "WWE 2K14" },469{ "ULUS10543", "WWE Smackdown vs. Raw 2011" },470{ "ULUS10423", "Warriors Orochi 2" },471{ "ULJM05553", "Warship Gunner 2 Portable" },472{ "ULJS00155", "Way Of The Samurai" },473{ "UCES00465", "Wipeout Pulse" },474{ "ULUS10321", "World Series of Poker 2008 - Battle for the Bracelets" },475{ "NPUH10045", "Worms Battle Islands" },476{ "ULUS10065", "Worms Open Warfare" },477{ "ULUS10260", "Worms Open Warfare 2" },478{ "ULJM05734", "Yu-Gi-Oh! 5D's Tag Force 5" },479{ "ULJM05940", "Yu-Gi-Oh! 5D's Tag Force 6" },480{ "NPJH00142", "Yu-Gi-Oh! Arc-V Tag Force" },481{ "ULJM05151", "Yu-Gi-Oh! GX Tag Force" },482{ "ULJM05373", "Yu-Gi-Oh! GX Tag Force 3" },483{ "NPUG80086", "flOw" },484};485486// Function Prototypes487const char * strcpyxml(char * out, const char * in, uint32_t size);488489// Function Prototypes490void interrupt(int sig);491void enable_address_reuse(int fd);492void enable_keepalive(int fd);493void change_nodelay_mode(int fd, int flag);494void change_blocking_mode(int fd, int nonblocking);495int create_listen_socket(uint16_t port);496int server_loop(int server);497498void __AdhocServerInit() {499// Database Product name will update if new game region played on my server to list possible crosslinks500productids = std::vector<db_productid>(default_productids, default_productids + ARRAY_SIZE(default_productids));501crosslinks = std::vector<db_crosslink>(default_crosslinks, default_crosslinks + ARRAY_SIZE(default_crosslinks));502}503504/**505* Login User into Database (Stream)506* @param fd Socket507* @param ip IP Address (Network Order)508*/509void login_user_stream(int fd, uint32_t ip)510{511// Enough Space available512if(_db_user_count < SERVER_USER_MAXIMUM)513{514// Check IP Duplication515SceNetAdhocctlUserNode * u = _db_user;516while(u != NULL && u->resolver.ip != ip) u = u->next;517518if (u != NULL) { // IP Already existed519WARN_LOG(Log::sceNet, "AdhocServer: Already Existing IP: %s\n", ip2str(*(in_addr*)&u->resolver.ip).c_str());520}521522// Unique IP Address523else //if(u == NULL)524{525// Allocate User Node Memory526SceNetAdhocctlUserNode * user = (SceNetAdhocctlUserNode *)malloc(sizeof(SceNetAdhocctlUserNode));527528// Allocated User Node Memory529if(user != NULL)530{531// Clear Memory532memset(user, 0, sizeof(SceNetAdhocctlUserNode));533534// Save Socket535user->stream = fd;536537// Save IP538user->resolver.ip = ip;539540// Link into User List541user->next = _db_user;542if(_db_user != NULL) _db_user->prev = user;543_db_user = user;544545// Initialize Death Clock546user->last_recv = time(NULL);547548// Notify User549INFO_LOG(Log::sceNet, "AdhocServer: New Connection from %s", ip2str(*(in_addr*)&user->resolver.ip).c_str());550551// Fix User Counter552_db_user_count++;553554// Update Status Log555update_status();556557// Exit Function558return;559}560}561}562563// Duplicate IP, Allocation Error or not enough space - Close Stream564closesocket(fd);565}566567/**568* Login User into Database (Login Data)569* @param user User Node570* @param data Login Packet571*/572void login_user_data(SceNetAdhocctlUserNode * user, SceNetAdhocctlLoginPacketC2S * data)573{574// Product Code Check575int valid_product_code = 1;576577// Iterate Characters578int i = 0; for(; i < PRODUCT_CODE_LENGTH && valid_product_code == 1; i++)579{580// Valid Characters581if(!((data->game.data[i] >= 'A' && data->game.data[i] <= 'Z') || (data->game.data[i] >= '0' && data->game.data[i] <= '9'))) valid_product_code = 0;582}583584// Valid Packet Data585if(valid_product_code == 1 && memcmp(&data->mac, "\xFF\xFF\xFF\xFF\xFF\xFF", sizeof(data->mac)) != 0 && memcmp(&data->mac, "\x00\x00\x00\x00\x00\x00", sizeof(data->mac)) != 0 && data->name.data[0] != 0)586{587// Check for duplicated MAC as most games identify Players by MAC588SceNetAdhocctlUserNode* u = _db_user;589while (u != NULL && !IsMatch(u->resolver.mac, data->mac)) u = u->next;590591if (u != NULL) { // MAC Already existed592WARN_LOG(Log::sceNet, "AdhocServer: Already Existing MAC: %s [%s]\n", mac2str(&data->mac).c_str(), ip2str(*(in_addr*)&u->resolver.ip).c_str());593}594595// Game Product Override596game_product_override(&data->game);597598// Find existing Game599SceNetAdhocctlGameNode * game = _db_game;600while(game != NULL && strncmp(game->game.data, data->game.data, PRODUCT_CODE_LENGTH) != 0) game = game->next;601602// Game not found603if(game == NULL)604{605// Allocate Game Node Memory606game = (SceNetAdhocctlGameNode *)malloc(sizeof(SceNetAdhocctlGameNode));607608// Allocated Game Node Memory609if(game != NULL)610{611// Clear Memory612memset(game, 0, sizeof(SceNetAdhocctlGameNode));613614// Save Game Product ID615game->game = data->game;616617// Link into Game List618game->next = _db_game;619if(_db_game != NULL) _db_game->prev = game;620_db_game = game;621}622}623624// Game now available625if(game != NULL)626{627// Save MAC628user->resolver.mac = data->mac;629630// Save Nickname631user->resolver.name = data->name;632633// Increase Player Count in Game Node634game->playercount++;635636// Link Game to Player637user->game = game;638639// Notify User640char safegamestr[10];641memset(safegamestr, 0, sizeof(safegamestr));642strncpy(safegamestr, game->game.data, PRODUCT_CODE_LENGTH);643INFO_LOG(Log::sceNet, "AdhocServer: %s (MAC: %s - IP: %s) started playing %s", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr);644645// Update Status Log646update_status();647648// Leave Function649return;650}651}652653// Invalid Packet Data654else655{656// Notify User657WARN_LOG(Log::sceNet, "AdhocServer: Invalid Login Packet Contents from %s", ip2str(*(in_addr*)&user->resolver.ip).c_str());658}659660// Logout User - Out of Memory or Invalid Arguments661logout_user(user);662}663664/**665* Logout User from Database666* @param user User Node667*/668void logout_user(SceNetAdhocctlUserNode * user)669{670// Disconnect from Group671if(user->group != NULL) disconnect_user(user);672673// Unlink Leftside (Beginning)674if(user->prev == NULL) _db_user = user->next;675676// Unlink Leftside (Other)677else user->prev->next = user->next;678679// Unlink Rightside680if(user->next != NULL) user->next->prev = user->prev;681682// Close Stream683closesocket(user->stream);684685// Playing User686if(user->game != NULL)687{688// Notify User689char safegamestr[10];690memset(safegamestr, 0, sizeof(safegamestr));691strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH);692INFO_LOG(Log::sceNet, "AdhocServer: %s (MAC: %s - IP: %s) stopped playing %s", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr);693694// Fix Game Player Count695user->game->playercount--;696697// Empty Game Node698if(user->game->playercount == 0)699{700// Unlink Leftside (Beginning)701if(user->game->prev == NULL) _db_game = user->game->next;702703// Unlink Leftside (Other)704else user->game->prev->next = user->game->next;705706// Unlink Rightside707if(user->game->next != NULL) user->game->next->prev = user->game->prev;708709// Free Game Node Memory710free(user->game);711}712}713714// Unidentified User715else716{717// Notify User718WARN_LOG(Log::sceNet, "AdhocServer: Dropped Connection to %s", ip2str(*(in_addr*)&user->resolver.ip).c_str());719}720721// Free Memory722free(user);723724// Fix User Counter725_db_user_count--;726727// Update Status Log728update_status();729}730731/**732* Free Database Memory733*/734void free_database()735{736// There are users playing737if(_db_user_count > 0)738{739// Send Shutdown Notice740spread_message(NULL, SERVER_SHUTDOWN_MESSAGE);741}742743// Iterate Users for Deletion744SceNetAdhocctlUserNode * user = _db_user;745while(user != NULL)746{747// Next User (for safe delete)748SceNetAdhocctlUserNode * next = user->next;749750// Logout User751logout_user(user);752753// Move Pointer754user = next;755}756}757758/**759* Connect User to Game Group760* @param user User Node761* @param group Group Name762*/763void connect_user(SceNetAdhocctlUserNode * user, SceNetAdhocctlGroupName * group)764{765// Group Name Check766int valid_group_name = 1;767{768// Iterate Characters769int i = 0; for(; i < ADHOCCTL_GROUPNAME_LEN && valid_group_name == 1; i++)770{771// End of Name772if(group->data[i] == 0) break;773774// A - Z775if(group->data[i] >= 'A' && group->data[i] <= 'Z') continue;776777// a - z778if(group->data[i] >= 'a' && group->data[i] <= 'z') continue;779780// 0 - 9781if(group->data[i] >= '0' && group->data[i] <= '9') continue;782783// Invalid Symbol784valid_group_name = 0;785}786}787788// Valid Group Name789if(valid_group_name == 1)790{791// User is disconnected792if(user->group == NULL)793{794// Find Group in Game Node795SceNetAdhocctlGroupNode * g = user->game->group;796while(g != NULL && strncmp((char *)g->group.data, (char *)group->data, ADHOCCTL_GROUPNAME_LEN) != 0) g = g->next;797798// BSSID Packet799SceNetAdhocctlConnectBSSIDPacketS2C bssid;800801// Set BSSID Opcode802bssid.base.opcode = OPCODE_CONNECT_BSSID;803804// Set Default BSSID805bssid.mac = user->resolver.mac;806807// No Group found808if(g == NULL)809{810// Allocate Group Memory811g = (SceNetAdhocctlGroupNode *)malloc(sizeof(SceNetAdhocctlGroupNode));812813// Allocated Group Memory814if(g != NULL)815{816// Clear Memory817memset(g, 0, sizeof(SceNetAdhocctlGroupNode));818819// Link Game Node820g->game = user->game;821822// Link Group Node823g->next = g->game->group;824if(g->game->group != NULL) g->game->group->prev = g;825g->game->group = g;826827// Copy Group Name828g->group = *group;829830// Increase Group Counter for Game831g->game->groupcount++;832}833}834835// Group now available836if(g != NULL)837{838// Iterate remaining Group Players839SceNetAdhocctlUserNode * peer = g->player;840while(peer != NULL)841{842// Connect Packet843SceNetAdhocctlConnectPacketS2C packet;844845// Clear Memory846// memset(&packet, 0, sizeof(packet));847848// Set Connect Opcode849packet.base.opcode = OPCODE_CONNECT;850851// Set Player Name852packet.name = user->resolver.name;853854// Set Player MAC855packet.mac = user->resolver.mac;856857// Set Player IP858packet.ip = user->resolver.ip;859860// Send Data861int iResult = (int)send(peer->stream, (const char*)&packet, sizeof(packet), MSG_NOSIGNAL);862if (iResult < 0) ERROR_LOG(Log::sceNet, "AdhocServer: connect_user[send peer] (Socket error %d)", errno);863864// Set Player Name865packet.name = peer->resolver.name;866867// Set Player MAC868packet.mac = peer->resolver.mac;869870// Set Player IP871packet.ip = peer->resolver.ip;872873// Send Data874iResult = (int)send(user->stream, (const char*)&packet, sizeof(packet), MSG_NOSIGNAL);875if (iResult < 0) ERROR_LOG(Log::sceNet, "AdhocServer: connect_user[send user] (Socket error %d)", errno);876877// Set BSSID878if(peer->group_next == NULL) bssid.mac = peer->resolver.mac;879880// Move Pointer881peer = peer->group_next;882}883884// Link User to Group885user->group_next = g->player;886if(g->player != NULL) g->player->group_prev = user;887g->player = user;888889// Link Group to User890user->group = g;891892// Increase Player Count893g->playercount++;894895// Send Network BSSID to User896int iResult = (int)send(user->stream, (const char*)&bssid, sizeof(bssid), MSG_NOSIGNAL);897if (iResult < 0) ERROR_LOG(Log::sceNet, "AdhocServer: connect_user[send user bssid] (Socket error %d)", errno);898899// Notify User900char safegamestr[10];901memset(safegamestr, 0, sizeof(safegamestr));902strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH);903char safegroupstr[9];904memset(safegroupstr, 0, sizeof(safegroupstr));905strncpy(safegroupstr, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN);906INFO_LOG(Log::sceNet, "AdhocServer: %s (MAC: %s - IP: %s) joined %s group %s", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr, safegroupstr);907908// Update Status Log909update_status();910911// Exit Function912return;913}914}915916// Already connected to another group917else918{919// Notify User920char safegamestr[10];921memset(safegamestr, 0, sizeof(safegamestr));922strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH);923char safegroupstr[9];924memset(safegroupstr, 0, sizeof(safegroupstr));925strncpy(safegroupstr, (char *)group->data, ADHOCCTL_GROUPNAME_LEN);926char safegroupstr2[9];927memset(safegroupstr2, 0, sizeof(safegroupstr2));928strncpy(safegroupstr2, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN);929WARN_LOG(Log::sceNet, "AdhocServer: %s (MAC: %s - IP: %s) attempted to join %s group %s without disconnecting from %s first", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr, safegroupstr, safegroupstr2);930}931}932933// Invalid Group Name934else935{936// Notify User937char safegamestr[10];938memset(safegamestr, 0, sizeof(safegamestr));939strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH);940char safegroupstr[9];941memset(safegroupstr, 0, sizeof(safegroupstr));942strncpy(safegroupstr, (char *)group->data, ADHOCCTL_GROUPNAME_LEN);943WARN_LOG(Log::sceNet, "AdhocServer: %s (MAC: %s - IP: %s) attempted to join invalid %s group %s", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr, safegroupstr);944}945946// Invalid State, Out of Memory or Invalid Group Name947logout_user(user);948}949950/**951* Disconnect User from Game Group952* @param user User Node953*/954void disconnect_user(SceNetAdhocctlUserNode * user)955{956// User is connected957if(user->group != NULL)958{959// Unlink Leftside (Beginning)960if(user->group_prev == NULL) user->group->player = user->group_next;961962// Unlink Leftside (Other)963else user->group_prev->group_next = user->group_next;964965// Unlink Rightside966if(user->group_next != NULL) user->group_next->group_prev = user->group_prev;967968// Fix Player Count969user->group->playercount--;970971// Iterate remaining Group Players972SceNetAdhocctlUserNode * peer = user->group->player;973while(peer != NULL)974{975// Disconnect Packet976SceNetAdhocctlDisconnectPacketS2C packet;977978// Clear Memory979// memset(&packet, 0, sizeof(packet));980981// Set Disconnect Opcode982packet.base.opcode = OPCODE_DISCONNECT;983984// Set User IP985packet.ip = user->resolver.ip;986987// Send Data988int iResult = (int)send(peer->stream, (const char*)&packet, sizeof(packet), MSG_NOSIGNAL);989if (iResult < 0) ERROR_LOG(Log::sceNet, "AdhocServer: disconnect_user[send peer] (Socket error %d)", errno);990991// Move Pointer992peer = peer->group_next;993}994995// Notify User996char safegamestr[10];997memset(safegamestr, 0, sizeof(safegamestr));998strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH);999char safegroupstr[9];1000memset(safegroupstr, 0, sizeof(safegroupstr));1001strncpy(safegroupstr, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN);1002INFO_LOG(Log::sceNet, "AdhocServer: %s (MAC: %s - IP: %s) left %s group %s", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr, safegroupstr);10031004// Empty Group1005if(user->group->playercount == 0)1006{1007// Unlink Leftside (Beginning)1008if(user->group->prev == NULL) user->group->game->group = user->group->next;10091010// Unlink Leftside (Other)1011else user->group->prev->next = user->group->next;10121013// Unlink Rightside1014if(user->group->next != NULL) user->group->next->prev = user->group->prev;10151016// Free Group Memory1017free(user->group);10181019// Decrease Group Counter in Game Node1020user->game->groupcount--;1021}10221023// Unlink from Group1024user->group = NULL;1025user->group_next = NULL;1026user->group_prev = NULL;10271028// Update Status Log1029update_status();10301031// Exit Function1032return;1033}10341035// Not in a game group1036else1037{1038// Notify User1039char safegamestr[10];1040memset(safegamestr, 0, sizeof(safegamestr));1041strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH);1042WARN_LOG(Log::sceNet, "AdhocServer: %s (MAC: %s - IP: %s) attempted to leave %s group without joining one first", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr);1043}10441045// Delete User1046logout_user(user);1047}10481049/**1050* Send Game Group List1051* @param user User Node1052*/1053void send_scan_results(SceNetAdhocctlUserNode * user)1054{1055// User is disconnected1056if(user->group == NULL)1057{1058// Iterate Groups1059SceNetAdhocctlGroupNode * group = user->game->group;1060for(; group != NULL; group = group->next)1061{1062// Scan Result Packet1063SceNetAdhocctlScanPacketS2C packet;10641065// Clear Memory1066// memset(&packet, 0, sizeof(packet));10671068// Set Opcode1069packet.base.opcode = OPCODE_SCAN;10701071// Set Group Name1072packet.group = group->group;10731074// Iterate Players in Network Group1075SceNetAdhocctlUserNode * peer = group->player;1076for(; peer != NULL; peer = peer->group_next)1077{1078// Found Network Founder1079if(peer->group_next == NULL)1080{1081// Set Group Host MAC1082packet.mac = peer->resolver.mac;1083}1084}10851086// Send Group Packet1087int iResult = (int)send(user->stream, (const char*)&packet, sizeof(packet), MSG_NOSIGNAL);1088if (iResult < 0) ERROR_LOG(Log::sceNet, "AdhocServer: send_scan_result[send user] (Socket error %d)", errno);1089}10901091// Notify Player of End of Scan1092uint8_t opcode = OPCODE_SCAN_COMPLETE;1093int iResult = (int)send(user->stream, (const char*)&opcode, 1, MSG_NOSIGNAL);1094if (iResult < 0) ERROR_LOG(Log::sceNet, "AdhocServer: send_scan_result[send peer complete] (Socket error %d)", errno);10951096// Notify User1097char safegamestr[10];1098memset(safegamestr, 0, sizeof(safegamestr));1099strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH);1100INFO_LOG(Log::sceNet, "AdhocServer: %s (MAC: %s - IP: %s) requested information on %d %s groups", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), user->game->groupcount, safegamestr);11011102// Exit Function1103return;1104}11051106// User in a game group1107else1108{1109// Notify User1110char safegamestr[10];1111memset(safegamestr, 0, sizeof(safegamestr));1112strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH);1113char safegroupstr[9];1114memset(safegroupstr, 0, sizeof(safegroupstr));1115strncpy(safegroupstr, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN);1116WARN_LOG(Log::sceNet, "AdhocServer: %s (MAC: %s - IP: %s) attempted to scan for %s groups without disconnecting from %s first", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr, safegroupstr);1117}11181119// Delete User1120logout_user(user);1121}11221123/**1124* Spread Chat Message in P2P Network1125* @param user Sender User Node1126* @param message Chat Message1127*/1128void spread_message(SceNetAdhocctlUserNode *user, const char *message)1129{1130// Global Notice1131if(user == NULL)1132{1133// Iterate Players1134for(user = _db_user; user != NULL; user = user->next)1135{1136// Player has access to chat1137if(user->group != NULL)1138{1139// Chat Packet1140SceNetAdhocctlChatPacketS2C packet;11411142// Clear Memory1143memset(&packet, 0, sizeof(packet));11441145// Set Chat Opcode1146packet.base.base.opcode = OPCODE_CHAT;11471148// Set Chat Message1149strcpy(packet.base.message, message);11501151// Send Data1152int iResult = (int)send(user->stream, (const char*)&packet, sizeof(packet), MSG_NOSIGNAL);1153if (iResult < 0) ERROR_LOG(Log::sceNet, "AdhocServer: spread_message[send user chat] (Socket error %d)", errno);1154}1155}11561157// Prevent NULL Error1158return;1159}11601161// User is connected1162else if(user->group != NULL)1163{1164// Broadcast Range Counter1165uint32_t counter = 0;11661167// Iterate Group Players1168SceNetAdhocctlUserNode * peer = user->group->player;1169while(peer != NULL)1170{1171// Skip Self1172if(peer == user)1173{1174// Move Pointer1175peer = peer->group_next;11761177// Continue Loop1178continue;1179}11801181// Chat Packet1182SceNetAdhocctlChatPacketS2C packet;11831184// Set Chat Opcode1185packet.base.base.opcode = OPCODE_CHAT;11861187// Set Chat Message1188strcpy(packet.base.message, message);11891190// Set Sender Nickname1191packet.name = user->resolver.name;11921193// Send Data1194int iResult = (int)send(peer->stream, (const char*)&packet, sizeof(packet), MSG_NOSIGNAL);1195if (iResult < 0) ERROR_LOG(Log::sceNet, "AdhocServer: spread_message[send peer chat] (Socket error %d)", errno);11961197// Move Pointer1198peer = peer->group_next;11991200// Increase Broadcast Range Counter1201counter++;1202}12031204// Message Sent1205if(counter > 0)1206{1207// Notify User1208char safegamestr[10];1209memset(safegamestr, 0, sizeof(safegamestr));1210strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH);1211char safegroupstr[9];1212memset(safegroupstr, 0, sizeof(safegroupstr));1213strncpy(safegroupstr, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN);1214INFO_LOG(Log::sceNet, "AdhocServer: %s (MAC: %s - IP: %s) sent \"%s\" to %d players in %s group %s", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), message, counter, safegamestr, safegroupstr);1215}12161217// Exit Function1218return;1219}12201221// User not in a game group1222else1223{1224// Notify User1225char safegamestr[10];1226memset(safegamestr, 0, sizeof(safegamestr));1227strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH);1228WARN_LOG(Log::sceNet, "AdhocServer: %s (MAC: %s - IP: %s) attempted to send a text message without joining a %s group first", (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str(), safegamestr);1229}12301231// Delete User1232logout_user(user);1233}12341235/**1236* Get User State1237* @param user User Node1238*/1239int get_user_state(SceNetAdhocctlUserNode * user)1240{1241// Timeout Status1242if((time(NULL) - user->last_recv) >= SERVER_USER_TIMEOUT) return USER_STATE_TIMED_OUT;12431244// Waiting Status1245if(user->game == NULL) return USER_STATE_WAITING;12461247// Logged-In Status1248return USER_STATE_LOGGED_IN;1249}12501251/**1252* Clear RX Buffer1253* @param user User Node1254* @param clear Number of Bytes to clear (-1 for all)1255*/1256void clear_user_rxbuf(SceNetAdhocctlUserNode * user, int clear)1257{1258// Fix Clear Length1259if(clear == -1 || clear > (int)user->rxpos) clear = user->rxpos;12601261// Move Buffer1262memmove(user->rx, user->rx + clear, sizeof(user->rx) - clear);12631264// Fix RX Buffer Pointer1265user->rxpos -= clear;1266}12671268/**1269* Patch Game Product Code1270* @param product To-be-patched Product Code1271* @param from If the Product Code matches this...1272* @param to ... then change it to this one.1273*/1274void game_product_relink(SceNetAdhocctlProductCode * product, char * from, char * to)1275{1276// Relink Region Code1277if(strncmp(product->data, from, PRODUCT_CODE_LENGTH) == 0) strncpy(product->data, to, PRODUCT_CODE_LENGTH);1278}12791280/**1281* Game Product Override (used for mixing multi-region games)1282* @param product IN: Source Product OUT: Override Product1283*/1284void game_product_override(SceNetAdhocctlProductCode * product)1285{1286// Safe Product Code1287char productid[PRODUCT_CODE_LENGTH + 1];12881289// Prepare Safe Product Code1290strncpy(productid, product->data, PRODUCT_CODE_LENGTH);1291productid[PRODUCT_CODE_LENGTH] = 0;12921293// Database Handle1294//sqlite3 * db = NULL;12951296// Open Database1297//if(sqlite3_open(SERVER_DATABASE, &db) == SQLITE_OK)1298{1299// Crosslinked Flag1300int crosslinked = 0;13011302// Exists Flag1303int exists = 0;13041305// SQL Statements1306/*const char * sql = "SELECT id_to FROM crosslinks WHERE id_from=?;";1307const char * sql2 = "SELECT * FROM productids WHERE id=?;";1308const char * sql3 = "INSERT INTO productids(id, name) VALUES(?, ?);";13091310// Prepared SQL Statement1311sqlite3_stmt * statement = NULL;13121313// Prepare SQL Statement1314if(sqlite3_prepare_v2(db, sql, strlen(sql) + 1, &statement, NULL) == SQLITE_OK)1315{1316// Bind SQL Statement Data1317if(sqlite3_bind_text(statement, 1, productid, strlen(productid), SQLITE_STATIC) == SQLITE_OK)1318{1319// Found Matching Row1320if(sqlite3_step(statement) == SQLITE_ROW)1321{1322// Grab Crosslink ID1323const char * crosslink = (const char *)sqlite3_column_text(statement, 0);13241325// Crosslink Product Code1326strncpy(product->data, crosslink, PRODUCT_CODE_LENGTH);13271328// Log Crosslink1329INFO_LOG(Log::sceNet, "Crosslinked %s to %s", productid, crosslink);13301331// Set Crosslinked Flag1332crosslinked = 1;1333}1334}13351336// Destroy Prepared SQL Statement1337sqlite3_finalize(statement);1338}*/1339for (const auto &link : crosslinks) {1340if (IsMatch(link.id_from, productid)) {1341// Grab Crosslink ID1342char crosslink[PRODUCT_CODE_LENGTH + 1];1343strncpy(crosslink, link.id_to, PRODUCT_CODE_LENGTH);1344crosslink[PRODUCT_CODE_LENGTH] = 0; // null terminated13451346// Crosslink Product Code1347strncpy(product->data, link.id_to, PRODUCT_CODE_LENGTH);13481349// Log Crosslink1350INFO_LOG(Log::sceNet, "AdhocServer: Crosslinked %s to %s", productid, crosslink);13511352// Set Crosslinked Flag1353crosslinked = 1;1354break;1355}1356}13571358// Not Crosslinked1359if(!crosslinked)1360{1361// Prepare SQL Statement1362/*if(sqlite3_prepare_v2(db, sql2, strlen(sql2) + 1, &statement, NULL) == SQLITE_OK)1363{1364// Bind SQL Statement Data1365if(sqlite3_bind_text(statement, 1, productid, strlen(productid), SQLITE_STATIC) == SQLITE_OK)1366{1367// Found Matching Row1368if(sqlite3_step(statement) == SQLITE_ROW)1369{1370// Set Exists Flag1371exists = 1;1372}1373}13741375// Destroy Prepare SQL Statement1376sqlite3_finalize(statement);1377}*/1378for (const auto &product : productids) {1379if (IsMatch(product.id, productid)) {1380// Set Exists Flag1381exists = 1;1382break;1383}1384}13851386// Game doesn't exist in Database1387if(!exists)1388{1389// Prepare SQL Statement1390/*if(sqlite3_prepare_v2(db, sql3, strlen(sql3) + 1, &statement, NULL) == SQLITE_OK)1391{1392// Bind SQL Statement Data1393if(sqlite3_bind_text(statement, 1, productid, strlen(productid), SQLITE_STATIC) == SQLITE_OK && sqlite3_bind_text(statement, 2, productid, strlen(productid), SQLITE_STATIC) == SQLITE_OK)1394{1395// Save Product ID to Database1396if(sqlite3_step(statement) == SQLITE_DONE)1397{1398// Log Addition1399INFO_LOG(Log::sceNet, "Added Unknown Product ID %s to Database", productid);1400}1401}14021403// Destroy Prepare SQL Statement1404sqlite3_finalize(statement);1405}*/1406db_productid unkproduct;1407strncpy(unkproduct.id, productid, sizeof(unkproduct.id));1408strncpy(unkproduct.name, productid, sizeof(productid));1409productids.push_back(unkproduct); //productids[productids.size()] = unkproduct;1410// Log Addition1411INFO_LOG(Log::sceNet, "AdhocServer: Added Unknown Product ID %s to Database", productid);1412}1413}14141415// Close Database1416//sqlite3_close(db);1417}1418}14191420/**1421* Update Status Logfile1422*/1423void update_status()1424{1425// Open Logfile1426FILE * log = File::OpenCFile(Path(SERVER_STATUS_XMLOUT), "w");14271428// Opened Logfile1429if(log != NULL)1430{1431// Write XML Header1432fprintf(log, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");14331434// Write XSL Processor Information1435fprintf(log, "<?xml-stylesheet type=\"text/xsl\" href=\"status.xsl\"?>\n");14361437// Output Root Tag + User Count1438fprintf(log, "<prometheus usercount=\"%u\">\n", _db_user_count);14391440// Database Handle1441//sqlite3 * db = NULL;14421443// Open Database1444//if(sqlite3_open(SERVER_DATABASE, &db) == SQLITE_OK)1445{1446// Iterate Games1447SceNetAdhocctlGameNode * game = _db_game; for(; game != NULL; game = game->next)1448{1449// Safe Product ID1450char productid[PRODUCT_CODE_LENGTH + 1];1451strncpy(productid, game->game.data, PRODUCT_CODE_LENGTH);1452productid[PRODUCT_CODE_LENGTH] = 0;14531454// Display Name1455char displayname[128];1456memset(displayname, 0, sizeof(displayname));14571458// SQL Statement1459//const char * sql = "SELECT name FROM productids WHERE id=?;";14601461// Prepared SQL Statement1462//sqlite3_stmt * statement = NULL;14631464// Prepare SQL Statement1465/*if(sqlite3_prepare_v2(db, sql, strlen(sql) + 1, &statement, NULL) == SQLITE_OK)1466{1467// Bind SQL Statement Data1468if(sqlite3_bind_text(statement, 1, productid, strlen(productid), SQLITE_STATIC) == SQLITE_OK)1469{1470// Found Matching Row1471if(sqlite3_step(statement) == SQLITE_ROW)1472{1473// Fetch Game Name from Database1474const char * gamename = (const char *)sqlite3_column_text(statement, 0);14751476// Copy Game Name1477strcpyxml(displayname, gamename, sizeof(displayname));1478}14791480// Game not in Database1481else1482{1483// Use Product Code as Name1484strcpyxml(displayname, productid, sizeof(displayname));1485}1486}14871488// Destroy Prepared SQL Statement1489sqlite3_finalize(statement);1490}*/1491//db_productid *foundid = NULL;1492bool found = false;1493for (const auto &product : productids) {1494if (IsMatch(product.id, productid)) {1495// Copy Game Name1496strcpyxml(displayname, product.name, sizeof(displayname));1497found = true;1498break;1499}1500}15011502if (!found) {1503// Use Product Code as Name1504strcpyxml(displayname, productid, sizeof(displayname));1505}15061507// Output Game Tag + Game Name1508fprintf(log, "\t<game name=\"%s\" usercount=\"%u\">\n", displayname, game->playercount);15091510// Activate User Count1511uint32_t activecount = 0;15121513// Iterate Game Groups1514SceNetAdhocctlGroupNode * group = game->group; for(; group != NULL; group = group->next)1515{1516// Safe Group Name1517char groupname[ADHOCCTL_GROUPNAME_LEN + 1];1518strncpy(groupname, (const char *)group->group.data, ADHOCCTL_GROUPNAME_LEN);1519groupname[ADHOCCTL_GROUPNAME_LEN] = 0;15201521// Output Group Tag + Group Name + User Count1522fprintf(log, "\t\t<group name=\"%s\" usercount=\"%u\">\n", strcpyxml(displayname, groupname, sizeof(displayname)), group->playercount);15231524// Iterate Users1525SceNetAdhocctlUserNode * user = group->player; for(; user != NULL; user = user->group_next)1526{1527// Output User Tag + Username1528fprintf(log, "\t\t\t<user>%s</user>\n", strcpyxml(displayname, (const char *)user->resolver.name.data, sizeof(displayname)));1529}15301531// Output Closing Group Tag1532fprintf(log, "\t\t</group>\n");15331534// Increase Active Game User Count1535activecount += group->playercount;1536}15371538// Output Idle Game Group1539if(game->playercount > activecount)1540{1541// Output Group Tag + Group Name + Idle User Count1542fprintf(log, "\t\t<group name=\"Groupless\" usercount=\"%u\" />\n", game->playercount - activecount);1543}15441545// Output Closing Game Tag1546fprintf(log, "\t</game>\n");1547}15481549// Close Database1550//sqlite3_close(db);1551}15521553// Output Closing Root Tag1554fprintf(log, "</prometheus>");15551556// Close Logfile1557fclose(log);1558}1559}15601561/**1562* Escape XML Sequences to avoid malformed XML files.1563* @param out Out Buffer1564* @param in In Buffer1565* @param size Size of Out Buffer1566* @return Reference to Out Buffer1567*/1568const char * strcpyxml(char * out, const char * in, uint32_t size)1569{1570// Valid Arguments1571if(out != NULL && in != NULL && size > 0)1572{1573// Clear Memory1574memset(out, 0, size);15751576// Written Size Pointer1577uint32_t written = 0;15781579// Iterate In-Buffer Symbols1580uint32_t i = 0; for(; i < strlen(in); i++)1581{1582// " Symbol1583if(in[i] == '"')1584{1585// Enough Space in Out-Buffer (6B for ")1586if((size - written) > 6)1587{1588// Write Escaped Sequence1589strcpy(out + written, """);15901591// Move Pointer1592written += 6;1593}15941595// Truncate required1596else break;1597}15981599// < Symbol1600else if(in[i] == '<')1601{1602// Enough Space in Out-Buffer (4B for <)1603if((size - written) > 4)1604{1605// Write Escaped Sequence1606strcpy(out + written, "<");16071608// Move Pointer1609written += 4;1610}16111612// Truncate required1613else break;1614}16151616// > Symbol1617else if(in[i] == '>')1618{1619// Enough Space in Out-Buffer (4B for >)1620if((size - written) > 4)1621{1622// Write Escaped Sequence1623strcpy(out + written, ">");16241625// Move Pointer1626written += 4;1627}16281629// Truncate required1630else break;1631}16321633// & Symbol1634else if(in[i] == '&')1635{1636// Enough Space in Out-Buffer (5B for &)1637if((size - written) > 5)1638{1639// Write Escaped Sequence1640strcpy(out + written, "&");16411642// Move Pointer1643written += 5;1644}16451646// Truncate required1647else break;1648}16491650// Normal Character1651else1652{1653// Enough Space in Out-Buffer (1B)1654if((size - written) > 1)1655{1656// Write Character1657out[written++] = in[i];1658}1659}1660}16611662// Return Reference1663return out;1664}16651666// Invalid Arguments1667return NULL;1668}16691670/**1671* Server Entry Point1672* @param argc Number of Arguments1673* @param argv Arguments1674* @return OS Error Code1675*/1676int proAdhocServerThread(int port) // (int argc, char * argv[])1677{1678SetCurrentThreadName("AdhocServer");1679// Result1680int result = 0;16811682INFO_LOG(Log::sceNet, "AdhocServer: Begin of AdhocServer Thread");16831684// Create Signal Receiver for CTRL + C1685//signal(SIGINT, interrupt);16861687// Create Signal Receiver for kill / killall1688//signal(SIGTERM, interrupt);16891690// Create Listening Socket1691int server = create_listen_socket(port); //SERVER_PORT16921693// Created Listening Socket1694if(server != SOCKET_ERROR)1695{1696// Notify User1697INFO_LOG(Log::sceNet, "AdhocServer: Listening for Connections on TCP Port %u", port); //SERVER_PORT16981699// Port forward1700UPnP_Add(IP_PROTOCOL_TCP, port); // g_PortManager.Add(IP_PROTOCOL_TCP, port);17011702// Enter Server Loop1703result = server_loop(server);17041705// Remove Port mapping1706UPnP_Remove(IP_PROTOCOL_TCP, port); // g_PortManager.Remove(IP_PROTOCOL_TCP, port);17071708// Notify User1709INFO_LOG(Log::sceNet, "AdhocServer: Shutdown complete");1710}17111712//_status = 0;1713adhocServerRunning = false;17141715INFO_LOG(Log::sceNet, "AdhocServer: End of AdhocServer Thread");17161717// Return Result1718return result;1719}17201721/**1722* Server Shutdown Request Handler1723* @param sig Captured Signal1724*/1725void interrupt(int sig)1726{1727// Notify User1728INFO_LOG(Log::sceNet, "AdhocServer: Shutting down... please wait");17291730// Trigger Shutdown1731//_status = 0;1732adhocServerRunning = false;1733}17341735/**1736* Enable Address Reuse on Socket1737* @param fd Socket1738*/1739void enable_address_reuse(int fd)1740{1741// Enable Port Reuse1742setSockReuseAddrPort(fd);1743}17441745/**1746* Enable KeepAlive on Socket1747* @param fd Socket1748*/1749void enable_keepalive(int fd)1750{1751// Enable Value1752int on = 1;17531754// Enable Port Reuse1755setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (const char*)&on, sizeof(on));1756}17571758/**1759* Change TCP Socket TCP_NODELAY (Nagle Algo) mode1760* @param fd Socket1761* @param nonblocking 1 for Nonblocking, 0 for Blocking1762*/1763void change_nodelay_mode(int fd, int flag)1764{1765int opt = flag;1766setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, sizeof(opt));1767}17681769/**1770* Change Socket Blocking Mode1771* @param fd Socket1772* @param nonblocking 1 for Nonblocking, 0 for Blocking1773*/1774void change_blocking_mode(int fd, int nonblocking)1775{1776unsigned long on = 1;1777unsigned long off = 0;1778#ifdef _WIN321779if (nonblocking){1780// Change to Non-Blocking Mode1781ioctlsocket(fd, FIONBIO, &on);1782}1783else {1784// Change to Blocking Mode1785ioctlsocket(fd, FIONBIO, &off);1786}1787#else1788// Change to Non-Blocking Mode1789if(nonblocking) fcntl(fd, F_SETFL, O_NONBLOCK);17901791// Change to Blocking Mode1792else1793{1794// Get Flags1795int flags = fcntl(fd, F_GETFL);17961797// Remove Non-Blocking Flag1798fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);1799}1800#endif1801}18021803/**1804* Create Port-Bound Listening Socket1805* @param port TCP Port1806* @return Socket Descriptor1807*/1808int create_listen_socket(uint16_t port)1809{1810// Create Socket1811int fd = (int)socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);18121813// Created Socket1814if(fd != -1)1815{1816// Ignore SIGPIPE when supported (ie. BSD/MacOS)1817setSockNoSIGPIPE(fd, 1);18181819// Enable KeepAlive1820enable_keepalive(fd);18211822// Enable Address Reuse1823enable_address_reuse(fd); // Shouldn't Reuse the port for built-in AdhocServer to prevent conflict with Dedicated AdhocServer18241825// Make Socket Nonblocking1826change_blocking_mode(fd, 1);18271828// Make TCP Socket send immediately1829change_nodelay_mode(fd, 1);18301831// Prepare Local Address Information1832struct sockaddr_in local;1833memset(&local, 0, sizeof(local));1834local.sin_family = AF_INET;1835local.sin_addr.s_addr = INADDR_ANY;1836local.sin_port = htons(port);18371838// Should only bind to specific IP for the 2nd or more instance of PPSSPP to prevent communication interference issue when sharing the same port. (ie. Capcom Classics Collection Remixed)1839if (PPSSPP_ID > 1) {1840local.sin_addr = g_localhostIP.in.sin_addr;1841}18421843// Bind Local Address to Socket1844int bindresult = bind(fd, (struct sockaddr *)&local, sizeof(local));18451846// Bound Local Address to Socket1847if(bindresult != SOCKET_ERROR)1848{1849// Switch Socket into Listening Mode1850listen(fd, SERVER_LISTEN_BACKLOG);18511852// Return Socket1853return fd;1854}18551856// Notify User1857else {1858ERROR_LOG(Log::sceNet, "AdhocServer: Bind returned %i (Socket error %d)", bindresult, errno);1859auto n = GetI18NCategory(I18NCat::NETWORKING);1860g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("AdhocServer Failed to Bind Port")) + " " + std::to_string(port));1861}18621863// Close Socket1864closesocket(fd);1865}18661867// Notify User1868else ERROR_LOG(Log::sceNet, "AdhocServer: Socket returned %i (Socket error %d)", fd, errno);18691870// Return Error1871return -1;1872}18731874/**1875* Server Main Loop1876* @param server Server Listening Socket1877* @return OS Error Code1878*/1879int server_loop(int server)1880{1881// Set Running Status1882//_status = 1;1883adhocServerRunning = true;18841885// Create Empty Status Logfile1886update_status();18871888// Handling Loop1889while (adhocServerRunning) //(_status == 1)1890{1891// Login Block1892{1893// Login Result1894int loginresult = 0;18951896// Login Processing Loop1897do1898{1899// Prepare Address Structure1900struct sockaddr_in addr;1901socklen_t addrlen = sizeof(addr);1902memset(&addr, 0, sizeof(addr));19031904// Accept Login Requests1905// loginresult = accept4(server, (struct sockaddr *)&addr, &addrlen, SOCK_NONBLOCK);19061907// Alternative Accept Approach (some Linux Kernel don't support the accept4 Syscall... wtf?)1908loginresult = accept(server, (struct sockaddr *)&addr, &addrlen);1909if(loginresult != -1)1910{1911// Switch Socket into Non-Blocking Mode1912change_blocking_mode(loginresult, 1);1913}19141915// Login User (Stream)1916if (loginresult != -1) {1917u32_le sip = addr.sin_addr.s_addr;1918/* // Replacing 127.0.0.x with Ethernet IP will cause issue with multiple-instance of localhost (127.0.0.x)1919if (sip == 0x0100007f) { //127.0.0.1 should be replaced with LAN/WAN IP whenever available1920char str[100];1921gethostname(str, 100);1922u8 *pip = (u8*)&sip;1923if (gethostbyname(str)->h_addrtype == AF_INET && gethostbyname(str)->h_addr_list[0] != NULL) pip = (u8*)gethostbyname(str)->h_addr_list[0];1924sip = *(u32_le*)pip;1925WARN_LOG(Log::sceNet, "AdhocServer: Replacing IP %s with %s", inet_ntoa(addr.sin_addr), inet_ntoa(*(in_addr*)&pip));1926}1927*/1928login_user_stream(loginresult, sip);1929}1930} while(loginresult != -1);1931}19321933// Receive Data from Users1934SceNetAdhocctlUserNode * user = _db_user;1935while(user != NULL)1936{1937// Next User (for safe delete)1938SceNetAdhocctlUserNode * next = user->next;19391940// Receive Data from User1941int recvresult = (int)recv(user->stream, (char*)user->rx + user->rxpos, sizeof(user->rx) - user->rxpos, MSG_NOSIGNAL);19421943// Connection Closed or Timed Out1944if(recvresult == 0 || (recvresult == -1 && errno != EAGAIN && errno != EWOULDBLOCK) || get_user_state(user) == USER_STATE_TIMED_OUT)1945{1946// Logout User1947logout_user(user);1948}19491950// Received Data (or leftovers in RX-Buffer)1951else if(recvresult > 0 || user->rxpos > 0)1952{1953// New Incoming Data1954if(recvresult > 0)1955{1956// Move RX Pointer1957user->rxpos += recvresult;19581959// Update Death Clock1960user->last_recv = time(NULL);1961}19621963// Waiting for Login Packet1964if(get_user_state(user) == USER_STATE_WAITING)1965{1966// Valid Opcode1967if(user->rx[0] == OPCODE_LOGIN)1968{1969// Enough Data available1970if(user->rxpos >= sizeof(SceNetAdhocctlLoginPacketC2S))1971{1972// Clone Packet1973SceNetAdhocctlLoginPacketC2S packet = *(SceNetAdhocctlLoginPacketC2S *)user->rx;19741975// Remove Packet from RX Buffer1976clear_user_rxbuf(user, sizeof(SceNetAdhocctlLoginPacketC2S));19771978// Login User (Data)1979login_user_data(user, &packet);1980}1981}19821983// Invalid Opcode1984else1985{1986// Notify User1987WARN_LOG(Log::sceNet, "AdhocServer: Invalid Opcode 0x%02X in Waiting State from %s", user->rx[0], ip2str(*(in_addr*)&user->resolver.ip).c_str());19881989// Logout User1990logout_user(user);1991}1992}19931994// Logged-In User1995else if(get_user_state(user) == USER_STATE_LOGGED_IN)1996{1997// Ping Packet1998if(user->rx[0] == OPCODE_PING)1999{2000// Delete Packet from RX Buffer2001clear_user_rxbuf(user, 1);2002}20032004// Group Connect Packet2005else if(user->rx[0] == OPCODE_CONNECT)2006{2007// Enough Data available2008if(user->rxpos >= sizeof(SceNetAdhocctlConnectPacketC2S))2009{2010// Cast Packet2011SceNetAdhocctlConnectPacketC2S * packet = (SceNetAdhocctlConnectPacketC2S *)user->rx;20122013// Clone Group Name2014SceNetAdhocctlGroupName group = packet->group;20152016// Remove Packet from RX Buffer2017clear_user_rxbuf(user, sizeof(SceNetAdhocctlConnectPacketC2S));20182019// Change Game Group2020connect_user(user, &group);2021}2022}20232024// Group Disconnect Packet2025else if(user->rx[0] == OPCODE_DISCONNECT)2026{2027// Remove Packet from RX Buffer2028clear_user_rxbuf(user, 1);20292030// Leave Game Group2031disconnect_user(user);2032}20332034// Network Scan Packet2035else if(user->rx[0] == OPCODE_SCAN)2036{2037// Remove Packet from RX Buffer2038clear_user_rxbuf(user, 1);20392040// Send Network List2041send_scan_results(user);2042}20432044// Chat Text Packet2045else if(user->rx[0] == OPCODE_CHAT)2046{2047// Enough Data available2048if(user->rxpos >= sizeof(SceNetAdhocctlChatPacketC2S))2049{2050// Cast Packet2051SceNetAdhocctlChatPacketC2S * packet = (SceNetAdhocctlChatPacketC2S *)user->rx;20522053// Clone Buffer for Message2054char message[64];2055memset(message, 0, sizeof(message));2056strncpy(message, packet->message, sizeof(message) - 1);20572058// Remove Packet from RX Buffer2059clear_user_rxbuf(user, sizeof(SceNetAdhocctlChatPacketC2S));20602061// Spread Chat Message2062spread_message(user, message);2063}2064}20652066// Invalid Opcode2067else2068{2069// Notify User2070WARN_LOG(Log::sceNet, "AdhocServer: Invalid Opcode 0x%02X in Logged-In State from %s (MAC: %s - IP: %s)", user->rx[0], (char *)user->resolver.name.data, mac2str(&user->resolver.mac).c_str(), ip2str(*(in_addr*)&user->resolver.ip).c_str());20712072// Logout User2073logout_user(user);2074}2075}2076}20772078// Move Pointer2079user = next;2080}20812082// Prevent needless CPU Overload (1ms Sleep)2083sleep_ms(10);20842085// Don't do anything if it's paused, otherwise the log will be flooded2086while (adhocServerRunning && Core_IsStepping() && coreState != CORE_POWERDOWN) sleep_ms(10);2087}20882089// Free User Database Memory2090free_database();20912092// Close Server Socket2093closesocket(server);20942095// Return Success2096return 0;2097}209820992100