/libgambatte/libretro/net_serial.cpp

https://github.com/libretro/gambatte-libretro · C++ · 296 lines · 262 code · 31 blank · 3 comment · 42 complexity · 4c3015b941b75ae54090666c435659bf MD5 · raw file

  1. #include "net_serial.h"
  2. #include "libretro.h"
  3. #include "gambatte_log.h"
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <unistd.h>
  7. #include <errno.h>
  8. #include <sys/types.h>
  9. #ifdef _WIN32
  10. #include <winsock2.h>
  11. #include <ws2tcpip.h>
  12. #else
  13. #include <sys/socket.h>
  14. #include <sys/ioctl.h>
  15. #include <netinet/in.h>
  16. #include <netdb.h>
  17. #endif
  18. NetSerial::NetSerial()
  19. : is_stopped_(true)
  20. , is_server_(false)
  21. , port_(12345)
  22. , hostname_()
  23. , server_fd_(-1)
  24. , sockfd_(-1)
  25. , lastConnectAttempt_(0)
  26. {
  27. }
  28. NetSerial::~NetSerial()
  29. {
  30. stop();
  31. }
  32. bool NetSerial::start(bool is_server, int port, const std::string& hostname)
  33. {
  34. stop();
  35. gambatte_log(RETRO_LOG_INFO, "Starting GameLink network %s on %s:%d\n",
  36. is_server ? "server" : "client", hostname.c_str(), port);
  37. is_server_ = is_server;
  38. port_ = port;
  39. hostname_ = hostname;
  40. is_stopped_ = false;
  41. return checkAndRestoreConnection(false);
  42. }
  43. void NetSerial::stop()
  44. {
  45. if (!is_stopped_) {
  46. gambatte_log(RETRO_LOG_INFO, "Stopping GameLink network\n");
  47. is_stopped_ = true;
  48. if (sockfd_ >= 0) {
  49. close(sockfd_);
  50. sockfd_ = -1;
  51. }
  52. if (server_fd_ >= 0) {
  53. close(server_fd_);
  54. server_fd_ = -1;
  55. }
  56. }
  57. }
  58. bool NetSerial::checkAndRestoreConnection(bool throttle)
  59. {
  60. if (is_stopped_) {
  61. return false;
  62. }
  63. if (sockfd_ < 0 && throttle) {
  64. clock_t now = clock();
  65. // Only attempt to establish the connection every 5 seconds
  66. if (((now - lastConnectAttempt_) / CLOCKS_PER_SEC) < 5) {
  67. return false;
  68. }
  69. }
  70. lastConnectAttempt_ = clock();
  71. if (is_server_) {
  72. if (!startServerSocket()) {
  73. return false;
  74. }
  75. if (!acceptClient()) {
  76. return false;
  77. }
  78. } else {
  79. if (!startClientSocket()) {
  80. return false;
  81. }
  82. }
  83. return true;
  84. }
  85. bool NetSerial::startServerSocket()
  86. {
  87. int fd;
  88. struct sockaddr_in server_addr;
  89. if (server_fd_ < 0) {
  90. memset((char *)&server_addr, '\0', sizeof(server_addr));
  91. server_addr.sin_family = AF_INET;
  92. server_addr.sin_port = htons(port_);
  93. server_addr.sin_addr.s_addr = INADDR_ANY;
  94. int fd = socket(AF_INET, SOCK_STREAM, 0);
  95. if (fd < 0) {
  96. gambatte_log(RETRO_LOG_ERROR, "Error opening socket: %s\n", strerror(errno));
  97. return false;
  98. }
  99. if (bind(fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
  100. gambatte_log(RETRO_LOG_ERROR, "Error on binding: %s\n", strerror(errno));
  101. close(fd);
  102. return false;
  103. }
  104. if (listen(fd, 1) < 0) {
  105. gambatte_log(RETRO_LOG_ERROR, "Error listening: %s\n", strerror(errno));
  106. close(fd);
  107. return false;
  108. }
  109. server_fd_ = fd;
  110. gambatte_log(RETRO_LOG_INFO, "GameLink network server started!\n");
  111. }
  112. return true;
  113. }
  114. bool NetSerial::acceptClient()
  115. {
  116. struct sockaddr_in client_addr;
  117. struct timeval tv;
  118. fd_set rfds;
  119. if (server_fd_ < 0) {
  120. return false;
  121. }
  122. if (sockfd_ < 0) {
  123. int retval;
  124. FD_ZERO(&rfds);
  125. FD_SET(server_fd_, &rfds);
  126. tv.tv_sec = 0;
  127. tv.tv_usec = 0;
  128. if (select(server_fd_ + 1, &rfds, NULL, NULL, &tv) <= 0) {
  129. return false;
  130. }
  131. socklen_t client_len = sizeof(client_addr);
  132. sockfd_ = accept(server_fd_, (struct sockaddr*)&client_addr, &client_len);
  133. if (sockfd_ < 0) {
  134. gambatte_log(RETRO_LOG_ERROR, "Error on accept: %s\n", strerror(errno));
  135. return false;
  136. }
  137. gambatte_log(RETRO_LOG_INFO, "GameLink network server connected to client!\n");
  138. }
  139. return true;
  140. }
  141. bool NetSerial::startClientSocket()
  142. {
  143. int fd;
  144. struct sockaddr_in server_addr;
  145. if (sockfd_ < 0) {
  146. memset((char *)&server_addr, '\0', sizeof(server_addr));
  147. server_addr.sin_family = AF_INET;
  148. server_addr.sin_port = htons(port_);
  149. int fd = socket(AF_INET, SOCK_STREAM, 0);
  150. if (fd < 0) {
  151. gambatte_log(RETRO_LOG_ERROR, "Error opening socket: %s\n", strerror(errno));
  152. return false;
  153. }
  154. struct hostent* server_hostname = gethostbyname(hostname_.c_str());
  155. if (server_hostname == NULL) {
  156. gambatte_log(RETRO_LOG_ERROR, "Error, no such host: %s\n", hostname_.c_str());
  157. close(fd);
  158. return false;
  159. }
  160. memmove((char*)&server_addr.sin_addr.s_addr, (char*)server_hostname->h_addr, server_hostname->h_length);
  161. if (connect(fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
  162. gambatte_log(RETRO_LOG_ERROR, "Error connecting to server: %s\n", strerror(errno));
  163. close(fd);
  164. return false;
  165. }
  166. sockfd_ = fd;
  167. gambatte_log(RETRO_LOG_INFO, "GameLink network client connected to server!\n");
  168. }
  169. return true;
  170. }
  171. unsigned char NetSerial::send(unsigned char data, bool fastCgb)
  172. {
  173. unsigned char buffer[2];
  174. if (is_stopped_) {
  175. return 0xFF;
  176. }
  177. if (sockfd_ < 0) {
  178. if (!checkAndRestoreConnection(true)) {
  179. return 0xFF;
  180. }
  181. }
  182. buffer[0] = data;
  183. buffer[1] = fastCgb;
  184. #ifdef _WIN32
  185. if (::send(sockfd_, (char*) buffer, 2, 0) <= 0)
  186. #else
  187. if (write(sockfd_, buffer, 2) <= 0)
  188. #endif
  189. {
  190. gambatte_log(RETRO_LOG_ERROR, "Error writing to socket: %s\n", strerror(errno));
  191. close(sockfd_);
  192. sockfd_ = -1;
  193. return 0xFF;
  194. }
  195. #ifdef _WIN32
  196. if (recv(sockfd_, (char*) buffer, 2, 0) <= 0)
  197. #else
  198. if (read(sockfd_, buffer, 2) <= 0)
  199. #endif
  200. {
  201. gambatte_log(RETRO_LOG_ERROR, "Error reading from socket: %s\n", strerror(errno));
  202. close(sockfd_);
  203. sockfd_ = -1;
  204. return 0xFF;
  205. }
  206. return buffer[0];
  207. }
  208. bool NetSerial::check(unsigned char out, unsigned char& in, bool& fastCgb)
  209. {
  210. unsigned char buffer[2];
  211. #ifdef _WIN32
  212. u_long bytes_avail = 0;
  213. #else
  214. int bytes_avail = 0;
  215. #endif
  216. if (is_stopped_) {
  217. return false;
  218. }
  219. if (sockfd_ < 0) {
  220. if (!checkAndRestoreConnection(true)) {
  221. return false;
  222. }
  223. }
  224. #ifdef _WIN32
  225. if (ioctlsocket(sockfd_, FIONREAD, &bytes_avail) < 0)
  226. #else
  227. if (ioctl(sockfd_, FIONREAD, &bytes_avail) < 0)
  228. #endif
  229. {
  230. gambatte_log(RETRO_LOG_ERROR, "IOCTL Failed: %s\n", strerror(errno));
  231. return false;
  232. }
  233. // No data available yet
  234. if (bytes_avail < 2) {
  235. return false;
  236. }
  237. #ifdef _WIN32
  238. if (recv(sockfd_, (char*) buffer, 2, 0) <= 0)
  239. #else
  240. if (read(sockfd_, buffer, 2) <= 0)
  241. #endif
  242. {
  243. gambatte_log(RETRO_LOG_ERROR, "Error reading from socket: %s\n", strerror(errno));
  244. close(sockfd_);
  245. sockfd_ = -1;
  246. return false;
  247. }
  248. // slave_txn_cnt++;
  249. in = buffer[0];
  250. fastCgb = buffer[1];
  251. buffer[0] = out;
  252. buffer[1] = 128;
  253. #ifdef _WIN32
  254. if (::send(sockfd_, (char*) buffer, 2, 0) <= 0)
  255. #else
  256. if (write(sockfd_, buffer, 2) <= 0)
  257. #endif
  258. {
  259. gambatte_log(RETRO_LOG_ERROR, "Error writing to socket: %s\n", strerror(errno));
  260. close(sockfd_);
  261. sockfd_ = -1;
  262. return false;
  263. }
  264. return true;
  265. }