chuniio-brokenithm/chuniio/src/servers/android.c

468 lines
15 KiB
C

//
// Created by beerpsi on 12/31/2023.
//
#include "android.h"
#include "arch.h"
#ifdef ENV32BIT
#include <inttypes.h>
#include <process.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include "servers/common.h"
#include "servers/socket.h"
bool tcp_mode = true;
uint16_t server_port = 52468;
enum CardType {
CARD_AIME,
CARD_FELICA,
};
typedef struct {
SOCKET sock;
char remote_address[40];
uint16_t remote_port;
atomic_bool exit_flag;
atomic_bool connected;
uint32_t last_input_packet_id;
uint8_t last_card_id[10];
bool has_previous_led_status;
uint8_t previous_led_status[3 * 32];
uint8_t led_skip_count;
struct IPCMemoryInfo *memory;
} android_thread_ctx;
void socket_set_timeout(const SOCKET sHost, int timeout) {
setsockopt(sHost, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(int));
setsockopt(sHost, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(int));
}
int socket_bind(const SOCKET sHost, const unsigned long addr, const uint16_t port) {
struct sockaddr_in srcaddr = {};
memset(&srcaddr, 0, sizeof(srcaddr));
srcaddr.sin_family = AF_INET;
srcaddr.sin_addr.s_addr = addr;
srcaddr.sin_port = htons(port);
return bind(sHost, (struct sockaddr *)&srcaddr, sizeof(srcaddr));
}
int socket_send_to(const SOCKET sHost, const struct sockaddr_in *addr, const char *buf,
const int len) {
return sendto(sHost, buf, len, 0, (struct sockaddr *)&addr, sizeof(&addr));
}
int make_ipv4_address(struct sockaddr_in *addr, const char *host, const uint16_t port) {
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
return inet_pton(AF_INET, host, (struct in_addr *)&addr->sin_addr.s_addr);
}
void get_socks_address(const struct PacketConnect *pkt, char *address,
const int address_len, uint16_t *port) {
if (!pkt || !address || !port) {
return;
}
*port = ntohs(pkt->port);
switch (pkt->addrType) {
case 1:
inet_ntop(AF_INET, pkt->addr.addr4.addr, address, address_len);
break;
case 2:
inet_ntop(AF_INET6, pkt->addr.addr6, address, address_len);
break;
default:
return;
}
}
void print_card_info(const uint8_t card_type, const uint8_t *card_id) {
switch (card_type) {
case CARD_AIME:
print_err("[Android: INFO] Card type: AiMe, ID: ");
dump_bytes(card_id, 10, true);
break;
case CARD_FELICA:
print_err("[Android: INFO] Card type: FeliCa, ID: ");
dump_bytes(card_id, 8, true);
break;
default:
break;
}
}
void update_packet_id(android_thread_ctx *ctx, const uint32_t new_packet_id) {
if (ctx->last_input_packet_id > new_packet_id) {
print_err("[WARN] Packet #%" PRIu32 " came too late\n", new_packet_id);
} else if (new_packet_id > ctx->last_input_packet_id + 1) {
print_err("[WARN] Packets between #%" PRIu32 " and #%" PRIu32 " total %" PRIu32
" packet(s) are missing, probably too late or dropped\n",
ctx->last_input_packet_id, new_packet_id,
new_packet_id - ctx->last_input_packet_id - 1);
} else if (new_packet_id == ctx->last_input_packet_id) {
print_err("[WARN] Packet #%" PRIu32 " duplicated\n", new_packet_id);
}
ctx->last_input_packet_id = new_packet_id;
}
unsigned int __stdcall led_broadcast_thread_proc(void *v) {
android_thread_ctx *ctx = v;
const SOCKET sock = ctx->sock;
const struct IPCMemoryInfo *memory = ctx->memory;
struct sockaddr_in addr = {};
make_ipv4_address(&addr, ctx->remote_address, ctx->remote_port);
char send_buffer[4 + 3 * 32];
send_buffer[0] = 99;
send_buffer[1] = 'L';
send_buffer[2] = 'E';
send_buffer[3] = 'D';
while (!atomic_load(&ctx->exit_flag)) {
uint8_t current_led_status[3 * 32];
if (!atomic_load(&ctx->connected)) {
Sleep(50);
continue;
}
memcpy(current_led_status, memory->ledRgbData, 3 * 32);
bool same;
if (!ctx->has_previous_led_status) {
same = memcmp(ctx->previous_led_status, current_led_status, 3 * 32) == 0;
} else {
same = false;
}
memcpy(ctx->previous_led_status, current_led_status, 3 * 32);
ctx->has_previous_led_status = true;
if (!same || ++ctx->led_skip_count > 50) {
memcpy(send_buffer + 4, current_led_status, 3 * 32);
if (socket_send_to(sock, &addr, send_buffer, 100) < 0) {
print_err("[Android:ERROR] Cannot send packet: error %lu\n",
WSAGetLastError());
if (tcp_mode) {
if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
continue;
}
print_err("[Android: INFO] Device disconnected!\n");
ctx->connected = false;
ctx->exit_flag = true;
break;
}
}
ctx->led_skip_count = 0;
}
Sleep(10);
}
return 0;
}
unsigned int __stdcall input_recv_thread_proc(void *v) {
android_thread_ctx *ctx = v;
const SOCKET sock = ctx->sock;
struct IPCMemoryInfo *memory = ctx->memory;
struct sockaddr_in addr = {};
make_ipv4_address(&addr, ctx->remote_address, ctx->remote_port);
int recv_len, packet_len;
uint8_t real_len;
while (!atomic_load(&ctx->exit_flag)) {
char buffer[96];
if (!tcp_mode) {
/**
on UDP mode data is sent as packets, so just receive into a buffer big
enough for 1 packet each recvfrom call will only get 1 packet of data, the
remaining data is discarded
**/
if ((recv_len = recvfrom(sock, buffer, 96, 0, NULL, NULL)) == -1) {
continue;
}
real_len = (unsigned char)buffer[0];
if (real_len > recv_len) {
continue;
}
packet_len = real_len + 1;
} else {
/**
on TCP mode packets are length-prefixed, so we read in the first 4 bytes
to figure out how much we need to read, then read in the full data.
**/
recv_len = 0;
while (recv_len < 4) {
const int read = recv(sock, buffer + recv_len, 4 - recv_len, 0);
if (read == -1) {
int error = WSAGetLastError();
if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN ||
error == WSAETIMEDOUT) {
continue;
}
print_err("[Android: INFO] Device disconnected (could not read "
"data, errno "
"%d, os error %ld)\n",
errno, error);
atomic_store(&ctx->connected, false);
atomic_store(&ctx->exit_flag, true);
break;
}
recv_len = recv_len + read;
}
real_len = buffer[0];
packet_len = real_len + 1; // 1 for the packet length
while (recv_len < packet_len) {
const int read =
recv(sock, buffer + recv_len, packet_len - recv_len, 0);
if (read == -1) {
int error = WSAGetLastError();
if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN ||
error == WSAETIMEDOUT) {
continue;
}
print_err("[Android: INFO] Device disconnected (could not read "
"data, errno "
"%d, os error %ld)\n",
errno, error);
atomic_store(&ctx->connected, false);
atomic_store(&ctx->exit_flag, true);
break;
}
recv_len = recv_len + read;
}
}
if (packet_len >= sizeof(struct PacketInput) &&
memcmp(buffer + 1, "INP", 3) == 0) {
const struct PacketInput *pkt = (struct PacketInput *)buffer;
memcpy(memory->airIoStatus, pkt->airIoStatus, sizeof(pkt->airIoStatus));
memcpy(memory->sliderIoStatus, pkt->sliderIoStatus,
sizeof(pkt->sliderIoStatus));
memory->testBtn = pkt->testBtn;
memory->serviceBtn = pkt->serviceBtn;
update_packet_id(ctx, ntohl(pkt->packetId));
} else if (packet_len >= sizeof(struct PacketInputNoAir) &&
memcmp(buffer + 1, "IPT", 3) == 0) { // without air
const struct PacketInputNoAir *pkt = (struct PacketInputNoAir *)buffer;
memcpy(memory->sliderIoStatus, pkt->sliderIoStatus,
sizeof(pkt->sliderIoStatus));
memory->testBtn = pkt->testBtn;
memory->serviceBtn = pkt->serviceBtn;
update_packet_id(ctx, ntohl(pkt->packetId));
} else if (packet_len >= sizeof(struct PacketFunction) &&
memcmp(buffer + 1, "FNC", 3) == 0) {
const struct PacketFunction *pkt = (struct PacketFunction *)buffer;
switch (pkt->funcBtn) {
case FUNCTION_COIN:
memory->coinInsertion = 1;
break;
case FUNCTION_CARD:
memory->cardRead = 1;
break;
default:
break;
}
} else if (packet_len >= sizeof(struct PacketConnect) &&
memcmp(buffer + 1, "CON", 3) == 0) {
const struct PacketConnect *pkt = (struct PacketConnect *)buffer;
get_socks_address(pkt, ctx->remote_address, 40, &ctx->remote_port);
print_err("[Android: INFO] Device %s:%d connected.\n", ctx->remote_address,
ctx->remote_port);
ctx->last_input_packet_id = 0;
atomic_store(&ctx->connected, true);
} else if (packet_len >= 4 && memcmp(buffer + 1, "DIS", 3) == 0) {
atomic_store(&ctx->connected, false);
if (tcp_mode) {
atomic_store(&ctx->exit_flag, true);
print_err("[Android: INFO] Device disconnected (clean disconnect).\n");
break;
}
if (strlen(ctx->remote_address)) {
print_err("[Android: INFO] Device %s:%d disconnected.\n",
ctx->remote_address, ctx->remote_port);
memset(ctx->remote_address, 0, 40);
}
if (tcp_mode) {
break;
}
} else if (packet_len >= sizeof(struct PacketPing) &&
memcmp(buffer + 1, "PIN", 3) == 0) {
if (!atomic_load(&ctx->connected)) {
continue;
}
char response[13];
memcpy(response, buffer, 12);
response[2] = 'O';
socket_send_to(sock, &addr, response, 13);
} else if (packet_len >= sizeof(struct PacketCard) &&
memcmp(buffer + 1, "CRD", 3) == 0) {
const struct PacketCard *pkt = (struct PacketCard *)buffer;
if (pkt->remoteCardRead) {
if (memcmp(ctx->last_card_id, pkt->remoteCardId, 10) != 0) {
print_err("[Android: INFO] Got remote card.\n");
print_card_info(pkt->remoteCardType, pkt->remoteCardId);
memcpy(ctx->last_card_id, pkt->remoteCardId, 10);
}
} else if (memory->remoteCardRead) {
print_err("[Android: INFO] Remote card removed.\n");
memset(ctx->last_card_id, 0, 10);
}
memory->remoteCardRead = pkt->remoteCardRead;
memory->remoteCardType = pkt->remoteCardType;
memcpy(memory->remoteCardId, pkt->remoteCardId, 10);
}
}
free(ctx);
return 0;
}
unsigned int __stdcall server_thread_proc(void *v) {
struct IPCMemoryInfo *memory = v;
if (!tcp_mode) {
print_err("[Android: INFO] Mode: UDP\n");
const SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
socket_set_timeout(sock, 2000);
socket_bind(sock, htonl(INADDR_ANY), server_port);
print_err("[Android: INFO] Waiting for device on port %d...\n", server_port);
android_thread_ctx *ctx = malloc(sizeof(android_thread_ctx));
ctx->sock = sock;
ctx->exit_flag = ATOMIC_VAR_INIT(false);
ctx->connected = ATOMIC_VAR_INIT(false);
ctx->last_input_packet_id = 0;
ctx->memory = memory;
ctx->has_previous_led_status = false;
ctx->led_skip_count = 0;
HANDLE led_thread =
(HANDLE)_beginthreadex(NULL, 0, led_broadcast_thread_proc, ctx, 0, NULL);
HANDLE input_thread =
(HANDLE)_beginthreadex(NULL, 0, input_recv_thread_proc, ctx, 0, NULL);
WaitForSingleObject(led_thread, INFINITE);
WaitForSingleObject(input_thread, INFINITE);
CloseHandle(led_thread);
CloseHandle(input_thread);
free(ctx);
} else {
print_err("[Android: INFO] Mode: TCP\n");
const SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
socket_set_timeout(sock, 50);
socket_bind(sock, htonl(INADDR_ANY), server_port);
listen(sock, 10);
print_err("[Android: INFO] Waiting for device on port %d...\n", server_port);
struct sockaddr_in user_socket = {};
socklen_t sock_size = sizeof(struct sockaddr_in);
SOCKET acc_socket;
while (
(acc_socket = accept(sock, (struct sockaddr *)&user_socket, &sock_size))) {
char buffer[20] = {};
const char *user_address =
inet_ntop(AF_INET, &user_socket.sin_addr, buffer, 20);
if (user_address != NULL) {
print_err("[Android: INFO] Device %s:%d connected.\n", user_address,
user_socket.sin_port);
}
android_thread_ctx *ctx = malloc(sizeof(android_thread_ctx));
ctx->sock = acc_socket;
ctx->exit_flag = ATOMIC_VAR_INIT(false);
ctx->connected = ATOMIC_VAR_INIT(true);
ctx->last_input_packet_id = 0;
ctx->memory = memory;
ctx->has_previous_led_status = false;
ctx->led_skip_count = 0;
_beginthreadex(NULL, 0, led_broadcast_thread_proc, ctx, 0, NULL);
_beginthreadex(NULL, 0, input_recv_thread_proc, ctx, 0, NULL);
}
}
}
#endif // defined(ENV32BIT)
HRESULT android_init_server(struct IPCMemoryInfo *memory) {
#ifdef ENV32BIT
tcp_mode =
GetPrivateProfileIntW(L"brokenithm", L"tcp", 1, L".\\segatools.ini") == 1;
server_port =
GetPrivateProfileIntW(L"brokenithm", L"port", 52468, L".\\segatools.ini");
struct WSAData wsaData = {};
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
print_err("[Android:ERROR] WSA startup failed, os error %ld\n",
WSAGetLastError());
return E_FAIL;
}
_beginthreadex(NULL, 0, server_thread_proc, memory, 0, NULL);
#endif // defined(ENV32BIT)
return S_OK;
}