diff options
author | Olivia Mackintosh <livvy@base.nu> | 2020-04-21 18:33:32 +0100 |
---|---|---|
committer | Olivia Mackintosh <livvy@base.nu> | 2020-04-21 18:33:32 +0100 |
commit | ddd234ea1ff2924e587372aa12c4d5cda99a0ce0 (patch) | |
tree | f5c6f28a18b726d04fcbb351341df4fb05714385 /src/device.c | |
download | tiny-ddd234ea1ff2924e587372aa12c4d5cda99a0ce0.tar.gz |
Initial commit
Diffstat (limited to 'src/device.c')
-rw-r--r-- | src/device.c | 522 |
1 files changed, 522 insertions, 0 deletions
diff --git a/src/device.c b/src/device.c new file mode 100644 index 0000000..879d2a0 --- /dev/null +++ b/src/device.c @@ -0,0 +1,522 @@ +#include "device.h" +#include "track.h" +#include <assert.h> +#include <ctype.h> +#include <math.h> +#include <stdio.h> +#include <string.h> + +static int find_track(struct sync_device *d, const char *name) +{ + int i; + for (i = 0; i < (int)d->num_tracks; ++i) + if (!strcmp(name, d->tracks[i]->name)) + return i; + return -1; /* not found */ +} + +static const char *path_encode(const char *path) +{ + static char temp[FILENAME_MAX]; + int i, pos = 0; + int path_len = (int)strlen(path); + for (i = 0; i < path_len; ++i) { + int ch = path[i]; + if (isalnum(ch) || ch == '.' || ch == '_') { + if (pos >= sizeof(temp) - 1) + break; + + temp[pos++] = (char)ch; + } else { + if (pos >= sizeof(temp) - 3) + break; + + temp[pos++] = '-'; + temp[pos++] = "0123456789ABCDEF"[(ch >> 4) & 0xF]; + temp[pos++] = "0123456789ABCDEF"[ch & 0xF]; + } + } + + temp[pos] = '\0'; + return temp; +} + +static const char *sync_track_path(const char *base, const char *name) +{ + static char temp[FILENAME_MAX]; + strncpy(temp, base, sizeof(temp) - 1); + temp[sizeof(temp) - 1] = '\0'; + strncat(temp, "_", sizeof(temp) - strlen(temp) - 1); + strncat(temp, path_encode(name), sizeof(temp) - strlen(temp) - 1); + strncat(temp, ".track", sizeof(temp) - strlen(temp) - 1); + return temp; +} + +#ifndef SYNC_PLAYER + +#define CLIENT_GREET "hello, synctracker!" +#define SERVER_GREET "hello, demo!" + +enum { + SET_KEY = 0, + DELETE_KEY = 1, + GET_TRACK = 2, + SET_ROW = 3, + PAUSE = 4, + SAVE_TRACKS = 5 +}; + +static inline int socket_poll(SOCKET socket) +{ + struct timeval to = { 0, 0 }; + fd_set fds; + + FD_ZERO(&fds); + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4127) +#endif + FD_SET(socket, &fds); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + return select((int)socket + 1, &fds, NULL, NULL, &to) > 0; +} + +static inline int xsend(SOCKET s, const void *buf, size_t len, int flags) +{ +#ifdef WIN32 + assert(len <= INT_MAX); + return send(s, (const char *)buf, (int)len, flags) != (int)len; +#else + return send(s, (const char *)buf, len, flags) != (int)len; +#endif +} + +static inline int xrecv(SOCKET s, void *buf, size_t len, int flags) +{ +#ifdef WIN32 + assert(len <= INT_MAX); + return recv(s, (char *)buf, (int)len, flags) != (int)len; +#else + return recv(s, (char *)buf, len, flags) != (int)len; +#endif +} + +#ifdef USE_AMITCP +static struct Library *socket_base = NULL; +#endif + +static SOCKET server_connect(const char *host, unsigned short nport) +{ + SOCKET sock = INVALID_SOCKET; +#ifdef USE_GETADDRINFO + struct addrinfo *addr, *curr; + char port[6]; +#else + struct hostent *he; + char **ap; +#endif + +#ifdef WIN32 + static int need_init = 1; + if (need_init) { + WSADATA wsa; + if (WSAStartup(MAKEWORD(2, 0), &wsa)) + return INVALID_SOCKET; + need_init = 0; + } +#elif defined(USE_AMITCP) + if (!socket_base) { + socket_base = OpenLibrary("bsdsocket.library", 4); + if (!socket_base) + return INVALID_SOCKET; + } +#endif + +#ifdef USE_GETADDRINFO + + snprintf(port, sizeof(port), "%u", nport); + if (getaddrinfo(host, port, 0, &addr) != 0) + return INVALID_SOCKET; + + for (curr = addr; curr; curr = curr->ai_next) { + int family = curr->ai_family; + struct sockaddr *sa = curr->ai_addr; + int sa_len = (int)curr->ai_addrlen; + +#else + + he = gethostbyname(host); + if (!he) + return INVALID_SOCKET; + + for (ap = he->h_addr_list; *ap; ++ap) { + int family = he->h_addrtype; + struct sockaddr_in sin; + struct sockaddr *sa = (struct sockaddr *)&sin; + int sa_len = sizeof(*sa); + + sin.sin_family = he->h_addrtype; + sin.sin_port = htons(nport); + memcpy(&sin.sin_addr, *ap, he->h_length); + memset(&sin.sin_zero, 0, sizeof(sin.sin_zero)); + +#endif + + sock = socket(family, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) + continue; + + if (connect(sock, sa, sa_len) >= 0) { + char greet[128]; + + if (xsend(sock, CLIENT_GREET, strlen(CLIENT_GREET), 0) || + xrecv(sock, greet, strlen(SERVER_GREET), 0)) { + closesocket(sock); + sock = INVALID_SOCKET; + continue; + } + + if (!strncmp(SERVER_GREET, greet, strlen(SERVER_GREET))) + break; + } + + closesocket(sock); + sock = INVALID_SOCKET; + } + +#ifdef USE_GETADDRINFO + freeaddrinfo(addr); +#endif + + return sock; +} + +#else + +void sync_set_io_cb(struct sync_device *d, struct sync_io_cb *cb) +{ + d->io_cb.open = cb->open; + d->io_cb.read = cb->read; + d->io_cb.close = cb->close; +} + +#endif + +#ifdef NEED_STRDUP +static inline char *rocket_strdup(const char *str) +{ + char *ret = malloc(strlen(str) + 1); + if (ret) + strcpy(ret, str); + return ret; +} +#define strdup rocket_strdup +#endif + +struct sync_device *sync_create_device(const char *base) +{ + struct sync_device *d = malloc(sizeof(*d)); + if (!d) + return NULL; + + d->base = strdup(path_encode(base)); + if (!d->base) { + free(d); + return NULL; + } + + d->tracks = NULL; + d->num_tracks = 0; + +#ifndef SYNC_PLAYER + d->row = -1; + d->sock = INVALID_SOCKET; +#endif + + d->io_cb.open = (void *(*)(const char *, const char *))fopen; + d->io_cb.read = (size_t (*)(void *, size_t, size_t, void *))fread; + d->io_cb.close = (int (*)(void *))fclose; + + return d; +} + +void sync_destroy_device(struct sync_device *d) +{ + int i; + +#ifndef SYNC_PLAYER + if (d->sock != INVALID_SOCKET) + closesocket(d->sock); +#endif + + for (i = 0; i < (int)d->num_tracks; ++i) { + free(d->tracks[i]->name); + free(d->tracks[i]->keys); + free(d->tracks[i]); + } + free(d->tracks); + free(d->base); + free(d); + +#if defined(USE_AMITCP) && !defined(SYNC_PLAYER) + if (socket_base) { + CloseLibrary(socket_base); + socket_base = NULL; + } +#endif +} + +static int read_track_data(struct sync_device *d, struct sync_track *t) +{ + int i; + void *fp = d->io_cb.open(sync_track_path(d->base, t->name), "rb"); + if (!fp) + return -1; + + d->io_cb.read(&t->num_keys, sizeof(int), 1, fp); + t->keys = malloc(sizeof(struct track_key) * t->num_keys); + if (!t->keys) + return -1; + + for (i = 0; i < (int)t->num_keys; ++i) { + struct track_key *key = t->keys + i; + char type; + d->io_cb.read(&key->row, sizeof(int), 1, fp); + d->io_cb.read(&key->value, sizeof(float), 1, fp); + d->io_cb.read(&type, sizeof(char), 1, fp); + key->type = (enum key_type)type; + } + + d->io_cb.close(fp); + return 0; +} + +static int save_track(const struct sync_track *t, const char *path) +{ + int i; + FILE *fp = fopen(path, "wb"); + if (!fp) + return -1; + + fwrite(&t->num_keys, sizeof(int), 1, fp); + for (i = 0; i < (int)t->num_keys; ++i) { + char type = (char)t->keys[i].type; + fwrite(&t->keys[i].row, sizeof(int), 1, fp); + fwrite(&t->keys[i].value, sizeof(float), 1, fp); + fwrite(&type, sizeof(char), 1, fp); + } + + fclose(fp); + return 0; +} + +void sync_save_tracks(const struct sync_device *d) +{ + int i; + for (i = 0; i < (int)d->num_tracks; ++i) { + const struct sync_track *t = d->tracks[i]; + save_track(t, sync_track_path(d->base, t->name)); + } +} + +#ifndef SYNC_PLAYER + +static int fetch_track_data(struct sync_device *d, struct sync_track *t) +{ + unsigned char cmd = GET_TRACK; + uint32_t name_len; + + assert(strlen(t->name) <= UINT32_MAX); + name_len = htonl((uint32_t)strlen(t->name)); + + /* send request data */ + if (xsend(d->sock, (char *)&cmd, 1, 0) || + xsend(d->sock, (char *)&name_len, sizeof(name_len), 0) || + xsend(d->sock, t->name, (int)strlen(t->name), 0)) + { + closesocket(d->sock); + d->sock = INVALID_SOCKET; + return -1; + } + + return 0; +} + +static int handle_set_key_cmd(SOCKET sock, struct sync_device *data) +{ + uint32_t track, row; + union { + float f; + uint32_t i; + } v; + struct track_key key; + unsigned char type; + + if (xrecv(sock, (char *)&track, sizeof(track), 0) || + xrecv(sock, (char *)&row, sizeof(row), 0) || + xrecv(sock, (char *)&v.i, sizeof(v.i), 0) || + xrecv(sock, (char *)&type, 1, 0)) + return -1; + + track = ntohl(track); + v.i = ntohl(v.i); + + key.row = ntohl(row); + key.value = v.f; + + assert(type < KEY_TYPE_COUNT); + assert(track < data->num_tracks); + key.type = (enum key_type)type; + return sync_set_key(data->tracks[track], &key); +} + +static int handle_del_key_cmd(SOCKET sock, struct sync_device *data) +{ + uint32_t track, row; + + if (xrecv(sock, (char *)&track, sizeof(track), 0) || + xrecv(sock, (char *)&row, sizeof(row), 0)) + return -1; + + track = ntohl(track); + row = ntohl(row); + + assert(track < data->num_tracks); + return sync_del_key(data->tracks[track], row); +} + +int sync_tcp_connect(struct sync_device *d, const char *host, unsigned short port) +{ + int i; + if (d->sock != INVALID_SOCKET) + closesocket(d->sock); + + d->sock = server_connect(host, port); + if (d->sock == INVALID_SOCKET) + return -1; + + for (i = 0; i < (int)d->num_tracks; ++i) { + free(d->tracks[i]->keys); + d->tracks[i]->keys = NULL; + d->tracks[i]->num_keys = 0; + } + + for (i = 0; i < (int)d->num_tracks; ++i) { + if (fetch_track_data(d, d->tracks[i])) { + closesocket(d->sock); + d->sock = INVALID_SOCKET; + return -1; + } + } + return 0; +} + +int sync_connect(struct sync_device *d, const char *host, unsigned short port) +{ + return sync_tcp_connect(d, host, port); +} + +int sync_update(struct sync_device *d, int row, struct sync_cb *cb, + void *cb_param) +{ + if (d->sock == INVALID_SOCKET) + return -1; + + /* look for new commands */ + while (socket_poll(d->sock)) { + unsigned char cmd = 0, flag; + uint32_t new_row; + if (xrecv(d->sock, (char *)&cmd, 1, 0)) + goto sockerr; + + switch (cmd) { + case SET_KEY: + if (handle_set_key_cmd(d->sock, d)) + goto sockerr; + break; + case DELETE_KEY: + if (handle_del_key_cmd(d->sock, d)) + goto sockerr; + break; + case SET_ROW: + if (xrecv(d->sock, (char *)&new_row, sizeof(new_row), 0)) + goto sockerr; + if (cb && cb->set_row) + cb->set_row(cb_param, ntohl(new_row)); + break; + case PAUSE: + if (xrecv(d->sock, (char *)&flag, 1, 0)) + goto sockerr; + if (cb && cb->pause) + cb->pause(cb_param, flag); + break; + case SAVE_TRACKS: + sync_save_tracks(d); + break; + default: + fprintf(stderr, "unknown cmd: %02x\n", cmd); + goto sockerr; + } + } + + if (cb && cb->is_playing && cb->is_playing(cb_param)) { + if (d->row != row && d->sock != INVALID_SOCKET) { + unsigned char cmd = SET_ROW; + uint32_t nrow = htonl(row); + if (xsend(d->sock, (char*)&cmd, 1, 0) || + xsend(d->sock, (char*)&nrow, sizeof(nrow), 0)) + goto sockerr; + d->row = row; + } + } + return 0; + +sockerr: + closesocket(d->sock); + d->sock = INVALID_SOCKET; + return -1; +} + +#endif /* !defined(SYNC_PLAYER) */ + +static int create_track(struct sync_device *d, const char *name) +{ + struct sync_track *t; + assert(find_track(d, name) < 0); + + t = malloc(sizeof(*t)); + t->name = strdup(name); + t->keys = NULL; + t->num_keys = 0; + + d->num_tracks++; + d->tracks = realloc(d->tracks, sizeof(d->tracks[0]) * d->num_tracks); + d->tracks[d->num_tracks - 1] = t; + + return (int)d->num_tracks - 1; +} + +const struct sync_track *sync_get_track(struct sync_device *d, + const char *name) +{ + struct sync_track *t; + int idx = find_track(d, name); + if (idx >= 0) + return d->tracks[idx]; + + idx = create_track(d, name); + t = d->tracks[idx]; + +#ifndef SYNC_PLAYER + if (d->sock != INVALID_SOCKET) + fetch_track_data(d, t); + else +#endif + read_track_data(d, t); + + return t; +} |