// UDP Communication #if !defined(D2KCLIENT) && !defined(D2KSERVER) #error "Must define D2KCLIENT or D2KSERVER" #endif #include // atoi #include // std::stringstream #include // strerror // system specific includes #if defined(_WIN32) #include // socklength_t #elif defined(__linux__) #include // close #include // inet_ntoa #include // ioctl #elif defined(_NDS) #include #include #include #include #include #elif defined(_3DS) #include <3ds.h> #include #include #include #include #include #include #include #endif // shared (linux, nds, 3ds) defines #if defined(__linux__) || defined(_NDS) || defined(_3DS) #define NETerrno errno #define NETEADDRINUSE EADDRINUSE #define NETEWOULDBLOCK EWOULDBLOCK #define NETioctlsocket ioctl #define INVALID_SOCKET -1 #endif // system specific defines #if defined(_WIN32) #define NETerrno WSAGetLastError() #define NETEADDRINUSE WSAEADDRINUSE #define NETEWOULDBLOCK WSAEWOULDBLOCK #define NETclosesocket closesocket #define NETioctlsocket ioctlsocket #elif defined(__linux__) #define NETclosesocket close #define SOCKET_ERROR -1 #elif defined(_NDS) #define SOCKET_ERROR -1 #define NETclosesocket close #elif defined(_3DS) #define SOCKET_ERROR -1 #define NETclosesocket closesocket #endif #include "easylogging++Wrapper.h" #if defined(_NDS) || defined(_3DS) #include "core.h" #include "windows/commandWindow.h" #include "windows/configWindow.h" #endif #include "udp.h" #include "misc.h" namespace D2K {namespace UDP { bool non_blocking = true; bool connected = false; uint16_t port = DEFAULT_PORT; SOCKET socket_id = INVALID_SOCKET; #ifdef D2KCLIENT DS2KeyPacket packet = DS2KeyPacket{}; uint8_t profile = 0; std::string remote_ip = ""; #endif #if defined(D2KSERVER) struct sockaddr_in local_sockaddr{}; #endif struct sockaddr_in remote_sockaddr{}; void Init() { #ifdef _WIN32 WSADATA wsa_data; // Startup with Winsock version 2.2 WSAStartup(0x0202, &wsa_data); #endif } void DeInit() { Disconnect(); #ifdef _WIN32 WSACleanup(); #endif } bool IsConnected() { return connected; } int Connect() { return Connect(UDP::non_blocking, UDP::port); } int Connect(uint16_t _port) { return Connect(UDP::non_blocking, _port); } int Connect(bool _non_blocking, uint16_t _port) { if(EMULATOR) // Skip if emulating { LOG_N_TIMES(1, ERROR) << "Wifi disabled for emulator.\n"; return 1; } static bool wifi_not_connected_log{}; // Helps us not spam the log #if defined(_3DS) if(wifi_status == WIFI_NOT_CONNECTED) { if(!wifi_not_connected_log) { LOG(ERROR) << "Error: Wifi not connected. Is it enabled?\n"; wifi_not_connected_log = true; } return 1; } #endif wifi_not_connected_log = false; if(IsConnected()) // If already connected Disconnect(); // Disconnect first SetConfigPort(_port); // Set port UDP::non_blocking = _non_blocking; remote_sockaddr.sin_family = AF_INET; remote_sockaddr.sin_port = htons(GetPort()); #if defined(D2KSERVER) int sockaddrlength = sizeof(struct sockaddr_in); local_sockaddr = sockaddr_in{}; local_sockaddr.sin_family = AF_INET; local_sockaddr.sin_addr.s_addr = INADDR_ANY; local_sockaddr.sin_port = htons(GetPort()); #endif socket_id = socket(PF_INET, SOCK_DGRAM, 0); // create a socket if(socket_id == INVALID_SOCKET) { int err = NETerrno; LOG(ERROR) << "Error #" << err << " (socket): " << strerror(err) << "\n"; Disconnect(); return err; } #ifdef D2KSERVER // bind a local address and port if(bind(socket_id, (struct sockaddr*)&local_sockaddr, sockaddrlength) == SOCKET_ERROR) { int err = NETerrno; if(err == NETEADDRINUSE) LOG(ERROR) << "Error (bind) port #" << GetPort() << " currently in use\n"; else LOG(ERROR) << "Error #" << err << " (bind): " << strerror(err) << "\n"; Disconnect(); return err; } #endif #ifdef _3DS int flags = fcntl(socket_id, F_GETFL, 0); if(UDP::non_blocking && fcntl(socket_id, F_SETFL, flags | O_NONBLOCK)) { int err = NETerrno; LOG(ERROR) << "Error #" << err << " (fcntl): " << strerror(err) << "\n"; #else // set blocking mode if(NETioctlsocket(socket_id, FIONBIO, (unsigned long*)&UDP::non_blocking) == SOCKET_ERROR) { int err = NETerrno; LOG(ERROR) << "Error #" << err << " (NETioctlsocket): " << strerror(err) << "\n"; #endif Disconnect(); return err; } connected = true; LOG(INFO) << "Connected on UDP port #" << GetPort() << "\n"; return 0; } int Disconnect() { if(EMULATOR) // Skip if emulating return 0; if(IsConnected()) { // udp system disconnects even if NETclosesocket returns error connected = false; if(NETclosesocket(socket_id) == SOCKET_ERROR) { int err = NETerrno; LOG(ERROR) << "Error #" << err << " (NETclosesocket): " << strerror(err) << "\n"; return err; } LOG(INFO) << "UDP Disconnected\n"; } return 0; } int Send(const void* buffer, unsigned int length) { if(!IsConnected()) { LOG(ERROR) << "Error (UDP::Send): Not connected\n"; return -1; } else if(length <= 0) { LOG(ERROR) << "Error (UDP::Send) length: #" << length << "\n"; return -2; } else if(buffer == nullptr) { LOG(ERROR) << "Error (UDP::Send) Invalid pointer\n"; return -3; } else // Successful { int sockaddrlength = sizeof(struct sockaddr_in); if(sendto(socket_id, (const char*)buffer, length, 0, (struct sockaddr*)&remote_sockaddr, sockaddrlength) == SOCKET_ERROR) { int err = NETerrno; LOG(ERROR) << "Error #" << err << " (sendto): " << strerror(err) << "\n"; return err; } //LOG_EVERY_N(300, TRACE) << "Sent " << length << " bytes"; } return 0; } int Recv(void* buffer, unsigned int length) { return Recv(buffer, length, (struct sockaddr*)&remote_sockaddr); } int Recv(void* buffer, unsigned int length, struct sockaddr* _remote_sockaddr) { if(!IsConnected()) { LOG(ERROR) << "Error (UDP::Recv): Not connected\n"; return -1; } else if(length <= 0) { LOG(ERROR) << "Error (UDP::Recv) length: #" << length << "\n"; return -2; } else if(buffer == nullptr) { LOG(ERROR) << "Error (UDP::Recv) Invalid pointer\n"; return -3; } else if(_remote_sockaddr == nullptr) { LOG(ERROR) << "Error (UDP::Recv) _remote_sockaddr is NULL\n"; return -4; } else // Successful { socklen_t sockaddrlength = sizeof(struct sockaddr_in); if(recvfrom(socket_id, (char*)buffer, length, 0, _remote_sockaddr, &sockaddrlength) == SOCKET_ERROR) { int err = NETerrno; if(err != NETEWOULDBLOCK) LOG(ERROR) << "Error #" << err << " (recvfrom): " << strerror(err) << "\n"; return err; } //LOG_EVERY_N(300, TRACE) << "Received " << length << " bytes"; } return 0; } unsigned long GetLocalIP() { #if defined(_NDS) return DSiWifi_GetIP(); #elif defined(_3DS) return gethostid(); #elif defined(_WIN32) || defined(__linux__) return local_sockaddr.sin_addr.s_addr; #endif } std::string GetLocalIPString() { struct in_addr sin_addr; sin_addr.s_addr = GetLocalIP(); std::string IP = inet_ntoa(sin_addr); return IP; } unsigned long GetRemoteIP() { return remote_sockaddr.sin_addr.s_addr; } std::string GetRemoteIPString() { std::string IP = inet_ntoa(remote_sockaddr.sin_addr); return IP; } void SetRemoteIP(const std::string& text) { remote_sockaddr.sin_addr.s_addr = inet_addr(text.c_str()); } void SetRemoteIP(unsigned long ip) { remote_sockaddr.sin_addr.s_addr = ip; } uint16_t GetPort() { return UDP::port; } std::string GetPortString() { return D2K::ltos(GetPort()); } void SetConfigPort(const std::string& _port_string) { long _port = D2K::stol(_port_string); if(_port > UINT16_MAX) // if _port is invalid _port = 0; // set to 0, SetConfigPort will take care of it SetConfigPort((uint16_t)_port); } void SetConfigPort(const char* _port_char) { int _port = atoi(_port_char); if(_port > UINT16_MAX) // if _port is invalid _port = 0; // set to 0, SetConfigPort will take care of it SetConfigPort((uint16_t)_port); } void SetConfigPort(uint16_t _port) { // If port is 0, Use default port 9501 UDP::port = _port == 0 ? DEFAULT_PORT : _port; LOG(INFO) << "UDP Port set to #" << UDP::GetPort() << ".\n"; } #if defined(D2KCLIENT) void SendNormalSetting(DS2KeySingleInputSettingPacket setting) { Send(&setting, sizeof(DS2KeySingleInputSettingPacket)); LOG(TRACE) << "SendNormalSetting()\n"; } void SendCommand(uint8_t command) { if(command >= UDP::SETTINGS_PACKET_MAX_BUTTONS) // Valid range is 0 - 11 return; packet = DS2KeyPacket{}; // Clear the packet packet.type = UDP::PACKET::COMMAND; packet.profile = GetProfile(); packet.keys = command; Send(&packet, sizeof(DS2KeyPacket)); // Send packet LOG(TRACE) << "SendCommand(" << (int)command << ")\n"; } void Update(uint32_t keys, uint32_t keysTurbo, const touchPosition* touch_position, const circlePosition* circle_position, const circlePosition* cstick_position, const accelVector* accel_status, const angularRate* gyro_status, const uint8_t* slider_volume, const float* slider_3d, uint16_t keyboard) { if(EMULATOR) // Skip if emulating return; if(!UDP::IsConnected() // Skip if not connected && Connect()) // and can't connect return; ListenForServer(); // Listen for the server packet = DS2KeyPacket{}; // Clear the packet packet.type = UDP::PACKET::NORMAL; packet.profile = GetProfile(); packet.keys = keys; packet.keys_turbo = keysTurbo; if(touch_position != nullptr) // Touch status is active { packet.touch_x = touch_position->px; // Update x packet.touch_y = touch_position->py; // Update y } else // Touch status is inactive { packet.keys &= ~KEY_TOUCH; // Clear touch status } if(circle_position != nullptr) // Circle status is active { packet.circle_x = circle_position->dx; // Update x packet.circle_y = circle_position->dy; // Update y } if(cstick_position != nullptr) // Cstick status is active { packet.cstick_x = cstick_position->dx; // Update x packet.cstick_y = cstick_position->dy; // Update y } if(accel_status != nullptr) // Accel status is active { packet.accel_x = accel_status->x; // Update x packet.accel_y = accel_status->y; // Update y packet.accel_z = accel_status->z; // Update z } if(gyro_status != nullptr) // Gyro status is active { packet.gyro_x = gyro_status->x; // Update x packet.gyro_y = gyro_status->y; // Update y packet.gyro_z = gyro_status->z; // Update z } if(slider_volume != nullptr) // Volume slider is active { // raw value is 0-63, we convert to 0-100 packet.slider_volume = (uint8_t)((*slider_volume * 100) / 63); } if(slider_3d != nullptr) // 3D slider is active { // raw value is 0.0f-1.0f, we convert to 0-100 packet.slider_3d = (uint8_t)(*slider_3d * 100); } packet.keyboard = keyboard; Send(&packet, sizeof(DS2KeyPacket)); // Send packet //LOG_EVERY_N(300, TRACE) << "Sent UDP::Update()"; } void SendLookupPacket() { packet = DS2KeyPacket{}; // Clear the packet packet.type = UDP::PACKET::LOOKUP; // Set as a lookup packet Send(&packet, sizeof(DS2KeyPacket)); // Send the packet out //LOG_EVERY_N(10, TRACE) << "SendLookupPacket()"; } void RequestSettingsCommand() { packet = UDP::DS2KeyPacket{}; // Clear the packet packet.type = UDP::PACKET::COMMAND_SETTINGS; // Set as command settings packet packet.profile = UDP::GetProfile(); // Set profile UDP::Send(&packet, sizeof(UDP::DS2KeyPacket)); // Send the packet out LOG(TRACE) << "RequestSettingsCommand()\n"; } void RequestInputSettings() { packet = UDP::DS2KeyPacket{}; // Clear the packet packet.type = UDP::PACKET::INPUT_SETTINGS; // Set as input settings packet packet.profile = UDP::GetProfile(); // Set profile UDP::Send(&packet, sizeof(UDP::DS2KeyPacket)); // Send the packet out LOG(TRACE) << "RequestInputSettings()\n"; } void ServerLookup() { if(EMULATOR) // Skip if emulating return; if(!UDP::IsConnected() // Skip if not connected && Connect()) // and can't connect return; unsigned long saved_remote_ip = GetRemoteIP(); // Save the remote IP unsigned long LocalIP = GetLocalIP(); // Get the local IP SetRemoteIP(((LocalIP) & 0xFF) | // Setup the broadcast IP (((LocalIP >> 8) & 0xFF) << 8) | (((LocalIP >> 16) & 0xFF) << 16) | (0xFF << 24)); SendLookupPacket(); // Send the lookup packet // wait for 1 second for(int i = 0; i < 60; i++) { WaitForVBlank(); } if(Recv((char*)&packet, sizeof(DS2KeyPacket)) != 0) // Didn't receive anything { SetRemoteIP(saved_remote_ip); // Reset the remote IP } else if(packet.type == UDP::PACKET::LOOKUP) // Received a lookup packet { if(GetLocalIP() == GetRemoteIP()) // If it's from the local IP SetRemoteIP(saved_remote_ip); // Reset the remote IP } } void ListenForServer() { if(EMULATOR) // Skip if emulating return; if(!UDP::IsConnected() // Skip if not connected && Connect()) // and can't connect return; DS2KeyCommandSettingsPacket command_settings_packet = DS2KeyCommandSettingsPacket{}; // Large packet DS2KeyInputSettingsPacket* input_settings_packet_pointer = (DS2KeyInputSettingsPacket*)&command_settings_packet; // Received something if(UDP::Recv(&command_settings_packet, sizeof(DS2KeyCommandSettingsPacket)) == 0) { switch(command_settings_packet.type) { case UDP::PACKET::ALIVE: // Received a status query SendLookupPacket(); // Send the lookup packet break; case UDP::PACKET::COMMAND_SETTINGS: // Received Command Settings GUI::Command::ProcessCommandSettingsPacket(command_settings_packet); LOG(TRACE) << "Received UDP::PACKET::COMMAND_SETTINGS\n"; break; case UDP::PACKET::INPUT_SETTINGS: // Received Input Settings GUI::ConfigWindow::ProcessInputSettingsPacket(*input_settings_packet_pointer); LOG(TRACE) << "Received UDP::PACKET::INPUT_SETTINGS\n"; break; default: break; } } } uint8_t GetProfile() { return profile; } std::string GetProfileString() { return ltos(GetProfile()); } void SetProfile(const std::string& _profile) { SetProfile(D2K::stol(_profile)); } void SetProfile(const char* _profile) { SetProfile(atoi(_profile)); } void SetProfile(uint8_t _profile) { UDP::profile = _profile; } #elif defined(D2KSERVER) void SendCommandSettings(DS2KeyCommandSettingsPacket settings) { Send(&settings, sizeof(DS2KeyCommandSettingsPacket)); LOG(TRACE) << "SendCommandSettings()\n"; } void SendInputSettings(DS2KeyInputSettingsPacket settings) { Send(&settings, sizeof(DS2KeyInputSettingsPacket)); LOG(TRACE) << "SendInputSettings()\n"; } #endif }} // namespace D2K::UDP