diff --git a/Makefile b/Makefile index 8be348d63b..1edf308b1a 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,9 @@ endif ifeq ($(HAVE_NETPLAY), 1) OBJ += netplay.o +ifeq ($(HAVE_SOCKET_LEGACY), 1) + OBJ += netplay_compat.o +endif endif ifeq ($(HAVE_RSOUND), 1) diff --git a/netplay.c b/netplay.c index 459f8f93dc..bc62c7fc8a 100644 --- a/netplay.c +++ b/netplay.c @@ -33,8 +33,10 @@ #include #include #include +#ifndef SSNES_CONSOLE #include #endif +#endif #include "netplay.h" #include "general.h" @@ -44,6 +46,8 @@ #include #include +#include "netplay_compat.h" + // Checks if input port/index is controlled by netplay or not. static bool netplay_is_alive(netplay_t *handle); @@ -59,18 +63,6 @@ static void netplay_set_spectate_input(netplay_t *handle, int16_t input); static bool netplay_send_cmd(netplay_t *handle, uint32_t cmd, const void *data, size_t size); static bool netplay_get_cmd(netplay_t *handle); -#ifdef _WIN32 -// Woohoo, Winsock has headers from the STONE AGE! :D -#define close(x) closesocket(x) -#define CONST_CAST (const char*) -#define NONCONST_CAST (char*) -#else -#define CONST_CAST -#define NONCONST_CAST -#include -#include -#endif - #define PREV_PTR(x) ((x) == 0 ? handle->buffer_size - 1 : (x) - 1) #define NEXT_PTR(x) ((x + 1) % handle->buffer_size) @@ -218,13 +210,17 @@ static void log_connection(const struct sockaddr_storage *their_addr, { const struct sockaddr_storage *storage; const struct sockaddr_in *v4; +#ifndef HAVE_SOCKET_LEGACY const struct sockaddr_in6 *v6; +#endif } u; u.storage = their_addr; const char *str = NULL; char buf_v4[INET_ADDRSTRLEN] = {0}; +#ifndef HAVE_SOCKET_LEGACY char buf_v6[INET6_ADDRSTRLEN] = {0}; +#endif if (their_addr->ss_family == AF_INET) { @@ -237,6 +233,7 @@ static void log_connection(const struct sockaddr_storage *their_addr, getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in), buf_v4, sizeof(buf_v4), NULL, 0, NI_NUMERICHOST); } +#ifndef HAVE_SOCKET_LEGACY else if (their_addr->ss_family == AF_INET6) { str = buf_v6; @@ -248,6 +245,7 @@ static void log_connection(const struct sockaddr_storage *their_addr, getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in6), buf_v6, sizeof(buf_v6), NULL, 0, NI_NUMERICHOST); } +#endif if (str) { @@ -262,7 +260,7 @@ static bool init_tcp_socket(netplay_t *handle, const char *server, uint16_t port { struct addrinfo hints, *res = NULL; memset(&hints, 0, sizeof(hints)); -#ifdef _WIN32 // Lolol, no AF_UNSPEC, wtf. +#if defined(_WIN32) || defined(HAVE_SOCKET_LEGACY) hints.ai_family = AF_INET; #else hints.ai_family = AF_UNSPEC; @@ -354,7 +352,7 @@ static bool init_udp_socket(netplay_t *handle, const char *server, uint16_t port { struct addrinfo hints; memset(&hints, 0, sizeof(hints)); -#ifdef _WIN32 // Lolol, no AF_UNSPEC, wtf. +#if defined(_WIN32) || defined(HAVE_SOCKET_LEGACY) hints.ai_family = AF_INET; #else hints.ai_family = AF_UNSPEC; @@ -398,8 +396,13 @@ static bool init_udp_socket(netplay_t *handle, const char *server, uint16_t port return true; } -static bool init_socket(netplay_t *handle, const char *server, uint16_t port) +// Platform specific socket library init. +static bool init_network(void) { + static bool inited = false; + if (inited) + return true; + #ifdef _WIN32 WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) @@ -411,13 +414,20 @@ static bool init_socket(netplay_t *handle, const char *server, uint16_t port) signal(SIGPIPE, SIG_IGN); // Do not like SIGPIPE killing our app :( #endif + inited = true; + return true; +} + +static bool init_socket(netplay_t *handle, const char *server, uint16_t port) +{ + if (!init_network()) + return false; + if (!init_tcp_socket(handle, server, port, handle->spectate)) return false; - if (!handle->spectate) - { - if (!init_udp_socket(handle, server, port)) - return false; - } + if (!handle->spectate && !init_udp_socket(handle, server, port)) + return false; + return true; } diff --git a/netplay_compat.c b/netplay_compat.c new file mode 100644 index 0000000000..53c5035f73 --- /dev/null +++ b/netplay_compat.c @@ -0,0 +1,103 @@ +/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010-2012 - Hans-Kristian Arntzen + * + * Some code herein may be based on code found in BSNES. + * + * SSNES 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * SSNES 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 for more details. + * + * You should have received a copy of the GNU General Public License along with SSNES. + * If not, see . + */ + +#include "netplay_compat.h" + +#undef getaddrinfo +#undef freeaddrinfo +#undef sockaddr_storage +#undef addrinfo + +#if defined(_WIN32) && !defined(_XBOX) +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#elif defined(_XBOX) +#include +#else +#include +#include +#include +#include +#include +#endif + +#include +#include +#include + +#define addrinfo addrinfo_ssnes__ + +int getaddrinfo_ssnes__(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res) +{ + struct addrinfo *info = (struct addrinfo*)calloc(1, sizeof(*info)); + if (!info) + return -1; + + info->ai_family = PF_INET; + info->ai_socktype = hints->ai_socktype; + + struct sockaddr_in *in_addr = calloc(1, sizeof(*in_addr)); + if (!in_addr) + { + free(info); + return -1; + } + + info->ai_addrlen = sizeof(*in_addr); + + in_addr->sin_family = AF_INET; + in_addr->sin_port = htons(strtoul(service, NULL, 0)); + + if (!node && (hints->ai_flags & AI_PASSIVE)) + in_addr->sin_addr.s_addr = INADDR_ANY; + else if (node && isdigit(*node)) + in_addr->sin_addr.s_addr = inet_addr(node); + else if (node && !isdigit(*node)) + { + struct hostent *host = gethostbyname(node); + if (!host || !host->h_addr_list[0]) + goto error; + + in_addr->sin_addr.s_addr = inet_addr(host->h_addr_list[0]); + } + else + goto error; + + info->ai_addr = (struct sockaddr*)in_addr; + *res = info; + + return 0; + +error: + free(in_addr); + free(info); + return -1; +} + +void freeaddrinfo_ssnes__(struct addrinfo *res) +{ + free(res->ai_addr); + free(res); +} + diff --git a/netplay_compat.h b/netplay_compat.h new file mode 100644 index 0000000000..09960d6710 --- /dev/null +++ b/netplay_compat.h @@ -0,0 +1,83 @@ +/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010-2012 - Hans-Kristian Arntzen + * + * Some code herein may be based on code found in BSNES. + * + * SSNES 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * SSNES 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 for more details. + * + * You should have received a copy of the GNU General Public License along with SSNES. + * If not, see . + */ + +#ifndef NETPLAY_COMPAT_H__ +#define NETPLAY_COMPAT_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(_WIN32) +// Woohoo, Winsock has headers from the STONE AGE! :D +#define close(x) closesocket(x) +#define CONST_CAST (const char*) +#define NONCONST_CAST (char*) + +#else + +#define CONST_CAST +#define NONCONST_CAST +#include +#include + +#if defined(__CELLOS_LV2__) +#define close(x) socketclose(x) +#endif + +#endif + +// Compatibility layer for legacy or incomplete BSD socket implementations. +// Only for IPv4. Mostly useful for the consoles which do not support +// anything reasonably modern on the socket API side of things. + +#ifdef HAVE_SOCKET_LEGACY + +#define sockaddr_storage sockaddr_in +#define addrinfo addrinfo_ssnes__ +#define getaddrinfo(serv, port, hints, res) getaddrinfo_ssnes__(serv, port, hints, res) +#define freeaddrinfo(res) freeaddrinfo_ssnes__(res) + +struct addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + struct sockaddr *ai_addr; + char *ai_canonname; + struct addrinfo *ai_next; +}; + +int getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res); + +void freeaddrinfo(struct addrinfo *res); + +#ifndef AI_PASSIVE +#define AI_PASSIVE 1 +#endif + +#define ss_family sin_family + +// gai_strerror() not used, so we skip that. + +#endif +#endif + diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 9cc90e407c..610065bab6 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -33,7 +33,18 @@ fi check_lib THREADS -lpthread pthread_create check_lib DYLIB $DYLIB dlopen + check_lib NETPLAY -lc socket +if [ $HAVE_NETPLAY = yes ]; then + HAVE_GETADDRINFO=auto + check_lib GETADDRINFO -lc getaddrinfo + if [ $HAVE_GETADDRINFO = yes ]; then + HAVE_SOCKET_LEGACY=no + else + HAVE_SOCKET_LEGACY=yes + fi +fi + check_lib GETOPT_LONG -lc getopt_long if [ $HAVE_DYLIB = no ] && [ $HAVE_DYNAMIC = yes ]; then @@ -124,7 +135,7 @@ check_pkgconf PYTHON python3 add_define_make OS $OS # Creates config.mk and config.h. -VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL DYLIB GETOPT_LONG THREADS CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM X264RGB" +VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL DYLIB GETOPT_LONG THREADS CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY SOCKET_LEGACY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM X264RGB" create_config_make config.mk $VARS create_config_header config.h $VARS