-
-
Notifications
You must be signed in to change notification settings - Fork 231
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
289 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
#include "retroarch_cmd.h" | ||
|
||
#include <stdio.h> | ||
|
||
#include "udp.h" | ||
|
||
static const char *LOCALHOST_IP = "127.0.0.1"; | ||
static const int RETROARCH_CMD_UDP_PORT = 55355; | ||
|
||
int retroarch_cmd(const char *cmd) | ||
{ | ||
return udp_send(LOCALHOST_IP, RETROARCH_CMD_UDP_PORT, cmd); | ||
} | ||
|
||
int retroarch_get(const char *cmd, char *response, size_t response_size) | ||
{ | ||
return udp_send_receive(LOCALHOST_IP, RETROARCH_CMD_UDP_PORT, cmd, response, response_size); | ||
} | ||
|
||
int retroarch_quit(void) | ||
{ | ||
return retroarch_cmd("QUIT"); | ||
} | ||
|
||
int retroarch_toggleMenu(void) | ||
{ | ||
return retroarch_cmd("MENU_TOGGLE"); | ||
} | ||
|
||
int retroarch_getInfo(RetroArchInfo_t *info) | ||
{ | ||
char response[128]; | ||
if (retroarch_get("GET_INFO", response, sizeof(response)) == -1) { | ||
return -1; | ||
} | ||
|
||
// Parse response "GET_INFO %d %d NO" or "GET_INFO %d %d %d" | ||
int parsed = sscanf(response, "GET_INFO %u %u %d", &info->max_disk_slots, &info->disk_slot, &info->state_slot); | ||
|
||
if (parsed < 2) { | ||
return -1; | ||
} | ||
|
||
info->has_state_slot = parsed == 3; | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#ifndef UTILS_RETROARCH_CMD_H__ | ||
#define UTILS_RETROARCH_CMD_H__ | ||
|
||
#include <stdbool.h> | ||
#include <stddef.h> | ||
|
||
typedef struct RetroArchInfo { | ||
unsigned int max_disk_slots; | ||
unsigned int disk_slot; | ||
int state_slot; | ||
bool has_state_slot; | ||
} RetroArchInfo_t; | ||
|
||
int retroarch_cmd(const char *cmd); // Send RetroArch UDP command | ||
int retroarch_get(const char *cmd, char *response, size_t response_size); // Get RetroArch UDP command response | ||
|
||
int retroarch_quit(void); // RetroArch QUIT | ||
int retroarch_toggleMenu(void); // RetroArch MENU_TOGGLE | ||
int retroarch_getInfo(RetroArchInfo_t *info); // Get RetroArch info | ||
|
||
#endif // UTILS_RETROARCH_CMD_H__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
#include "udp.h" | ||
|
||
#include <arpa/inet.h> | ||
#include <errno.h> | ||
#include <stdio.h> | ||
#include <string.h> | ||
#include <sys/time.h> | ||
#include <unistd.h> | ||
|
||
static const int PING_TIMEOUT_MS = 100; | ||
static const int MAX_RETRIES = 3; | ||
static const int RETRY_DELAY_MS = 500; | ||
static const int RECV_TIMEOUT_MS = 60000; | ||
|
||
static int _init_socket(const char *ipAddress, int port, int *socket_fd, struct sockaddr_in *server_address) | ||
{ | ||
*socket_fd = socket(AF_INET, SOCK_DGRAM, 0); | ||
if (*socket_fd == -1) { | ||
perror("Socket creation failed"); | ||
return -1; | ||
} | ||
|
||
memset(server_address, 0, sizeof(*server_address)); | ||
server_address->sin_family = AF_INET; | ||
server_address->sin_port = htons(port); | ||
if (inet_pton(AF_INET, ipAddress, &server_address->sin_addr) <= 0) { | ||
perror("Invalid IP address"); | ||
close(*socket_fd); | ||
return -1; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static int _ping_socket(int socket_fd, struct sockaddr_in *server_address) | ||
{ | ||
const char *ping_message = "VERSION"; | ||
char response[32]; | ||
struct timeval timeout; | ||
timeout.tv_sec = PING_TIMEOUT_MS / 1000; | ||
timeout.tv_usec = (PING_TIMEOUT_MS % 1000) * 1000; | ||
|
||
// Set receive timeout | ||
if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) { | ||
perror("Failed to set socket receive timeout"); | ||
return -1; | ||
} | ||
|
||
// Send ping message | ||
if (sendto(socket_fd, ping_message, strlen(ping_message), 0, (struct sockaddr *)server_address, sizeof(*server_address)) == -1) { | ||
perror("Failed to send ping"); | ||
return -1; | ||
} | ||
|
||
// Wait for response | ||
ssize_t bytes_received = recvfrom(socket_fd, response, sizeof(response), 0, NULL, NULL); | ||
if (bytes_received == -1) { | ||
if (errno == EWOULDBLOCK || errno == EAGAIN) { | ||
fprintf(stderr, "Ping timeout\n"); | ||
} | ||
else { | ||
perror("Failed to receive ping response"); | ||
} | ||
return -1; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static int _udp_send(const char *ipAddress, int port, const char *message) | ||
{ | ||
int socket_fd; | ||
struct sockaddr_in server_address; | ||
|
||
if (_init_socket(ipAddress, port, &socket_fd, &server_address) == -1) { | ||
return -1; | ||
} | ||
|
||
if (_ping_socket(socket_fd, &server_address) == -1) { | ||
close(socket_fd); | ||
return -1; | ||
} | ||
|
||
if (sendto(socket_fd, message, strlen(message), 0, (struct sockaddr *)&server_address, sizeof(server_address)) == -1) { | ||
perror("Failed to send data"); | ||
close(socket_fd); | ||
return -1; | ||
} | ||
|
||
close(socket_fd); | ||
return 0; | ||
} | ||
|
||
static int _udp_send_receive(const char *ipAddress, int port, const char *message, char *response, size_t response_size) | ||
{ | ||
int socket_fd; | ||
struct sockaddr_in server_address; | ||
|
||
if (_init_socket(ipAddress, port, &socket_fd, &server_address) == -1) { | ||
return -1; | ||
} | ||
|
||
if (_ping_socket(socket_fd, &server_address) == -1) { | ||
close(socket_fd); | ||
return -1; | ||
} | ||
|
||
if (sendto(socket_fd, message, strlen(message), 0, (struct sockaddr *)&server_address, sizeof(server_address)) == -1) { | ||
perror("Failed to send data"); | ||
close(socket_fd); | ||
return -1; | ||
} | ||
|
||
// Set a longer timeout for receiving the response | ||
struct timeval timeout; | ||
timeout.tv_sec = RECV_TIMEOUT_MS / 1000; | ||
timeout.tv_usec = (RECV_TIMEOUT_MS % 1000) * 1000; | ||
|
||
if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) { | ||
perror("Failed to set socket receive timeout"); | ||
close(socket_fd); | ||
return -1; | ||
} | ||
|
||
ssize_t bytes_received = recvfrom(socket_fd, response, response_size, 0, NULL, NULL); | ||
if (bytes_received == -1) { | ||
perror("Failed to receive data"); | ||
close(socket_fd); | ||
return -1; | ||
} | ||
|
||
response[bytes_received] = '\0'; // Null-terminate the received string | ||
|
||
close(socket_fd); | ||
return 0; | ||
} | ||
|
||
int udp_send(const char *ipAddress, int port, const char *message) | ||
{ | ||
for (int attempt = 0; attempt < MAX_RETRIES; attempt++) { | ||
if (_udp_send(ipAddress, port, message) == 0) { | ||
return 0; // Success | ||
} | ||
usleep(RETRY_DELAY_MS * 1000); // Delay before retrying | ||
} | ||
return -1; // Failed after retries | ||
} | ||
|
||
int udp_send_receive(const char *ipAddress, int port, const char *message, char *response, size_t response_size) | ||
{ | ||
for (int attempt = 0; attempt < MAX_RETRIES; attempt++) { | ||
if (_udp_send_receive(ipAddress, port, message, response, response_size) == 0) { | ||
return 0; // Success | ||
} | ||
usleep(RETRY_DELAY_MS * 1000); // Delay before retrying | ||
} | ||
return -1; // Failed after retries | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#ifndef UTILS_UDP_H__ | ||
#define UTILS_UDP_H__ | ||
|
||
#include <stdbool.h> | ||
#include <stddef.h> | ||
|
||
int udp_send(const char *ipAddress, int port, const char *message); | ||
int udp_send_receive(const char *ipAddress, int port, const char *message, char *response, size_t response_size); | ||
|
||
#endif // UTILS_UDP_H__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,63 @@ | ||
#include <arpa/inet.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
|
||
#include "utils/udp.h" | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
char *ipAddress = "127.0.0.1"; // localhost | ||
int port = 55355; // default RetroArch CMD port | ||
char *message; // the message to send | ||
char *message; | ||
size_t response_size = 0; | ||
|
||
if (argc < 2) { | ||
fprintf(stderr, "Usage: %s <MESSAGE> [<IP> <PORT>]\n", argv[0]); | ||
if (argc < 2 || (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0))) { | ||
fprintf(stderr, "Usage: %s [-h <ipAddress>] [-p <port>] [-r <response_size>] <message>\n", argv[0]); | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
message = argv[1]; | ||
|
||
if (argc == 4) { | ||
ipAddress = argv[2]; | ||
port = atoi(argv[3]); | ||
} | ||
|
||
int socket_fd = socket(AF_INET, SOCK_DGRAM, 0); | ||
if (socket_fd == -1) { | ||
perror("Socket creation failed"); | ||
exit(EXIT_FAILURE); | ||
for (int i = 1; i < argc; i++) { | ||
if (strcmp(argv[i], "-h") == 0) { | ||
if (i + 1 < argc) { | ||
ipAddress = argv[i + 1]; | ||
i++; | ||
} | ||
} | ||
else if (strcmp(argv[i], "-p") == 0) { | ||
if (i + 1 < argc) { | ||
port = atoi(argv[i + 1]); | ||
i++; | ||
} | ||
} | ||
else if (strcmp(argv[i], "-r") == 0) { | ||
if (i + 1 < argc) { | ||
response_size = atoi(argv[i + 1]); | ||
i++; | ||
} | ||
} | ||
else { | ||
message = argv[i]; | ||
} | ||
} | ||
|
||
struct sockaddr_in server_address; | ||
memset(&server_address, 0, sizeof(server_address)); | ||
server_address.sin_family = AF_INET; | ||
server_address.sin_port = htons(port); | ||
if (inet_pton(AF_INET, ipAddress, &server_address.sin_addr) <= 0) { | ||
perror("Invalid IP address"); | ||
close(socket_fd); | ||
exit(EXIT_FAILURE); | ||
} | ||
if (response_size > 0) { | ||
char *response = (char *)malloc(response_size); | ||
if (response == NULL) { | ||
perror("Failed to allocate memory for response"); | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
ssize_t bytes_sent = sendto(socket_fd, message, strlen(message), 0, | ||
(struct sockaddr *)&server_address, sizeof(server_address)); | ||
if (udp_send_receive(ipAddress, port, message, response, response_size) == -1) { | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
if (bytes_sent == -1) { | ||
perror("Failed to send data"); | ||
close(socket_fd); | ||
printf("%s\n", response); | ||
free(response); | ||
} | ||
else if (udp_send(ipAddress, port, message) == -1) { | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
printf("Sent %d bytes to %s:%d\n", bytes_sent, ipAddress, port); | ||
|
||
close(socket_fd); | ||
|
||
return EXIT_SUCCESS; | ||
} |
Submodule RetroArch-patch
updated
9 files