aboutsummaryrefslogtreecommitdiff
path: root/client/libacron/private
diff options
context:
space:
mode:
authorTrumeet <yuuta@yuuta.moe>2022-07-20 18:12:22 -0700
committerTrumeet <yuuta@yuuta.moe>2022-07-20 18:12:22 -0700
commitb4afa06e383325f4a0c751a64ca896d769db07a8 (patch)
tree7647fdd73d4f487de778c27aea99bf890458b647 /client/libacron/private
parentda14a17298c67d83e6da4732f47304954acc26fc (diff)
downloadacron-b4afa06e383325f4a0c751a64ca896d769db07a8.tar
acron-b4afa06e383325f4a0c751a64ca896d769db07a8.tar.gz
acron-b4afa06e383325f4a0c751a64ca896d769db07a8.tar.bz2
acron-b4afa06e383325f4a0c751a64ca896d769db07a8.zip
libac: First Commit
Diffstat (limited to 'client/libacron/private')
-rw-r--r--client/libacron/private/config.h17
-rw-r--r--client/libacron/private/connection.h28
-rw-r--r--client/libacron/private/helpers.c64
-rw-r--r--client/libacron/private/helpers.h22
-rw-r--r--client/libacron/private/log.c52
-rw-r--r--client/libacron/private/log.h42
-rw-r--r--client/libacron/private/serializer.c572
-rw-r--r--client/libacron/private/serializer.h18
8 files changed, 815 insertions, 0 deletions
diff --git a/client/libacron/private/config.h b/client/libacron/private/config.h
new file mode 100644
index 0000000..3b670df
--- /dev/null
+++ b/client/libacron/private/config.h
@@ -0,0 +1,17 @@
+/*
+ * Created by yuuta on 7/19/22.
+ */
+
+#ifndef LIBAC_CONFIG_H
+#define LIBAC_CONFIG_H
+
+#include "libac.h"
+
+#include <json-c/json_tokener.h>
+#include <stdio.h>
+
+extern _Thread_local struct libac_config *config;
+
+#define AC_CHECK_INIT do { if (!config) return AC_E_NOT_INITIALIZED; } while(0)
+
+#endif /* LIBAC_CONFIG_H */
diff --git a/client/libacron/private/connection.h b/client/libacron/private/connection.h
new file mode 100644
index 0000000..1c1567f
--- /dev/null
+++ b/client/libacron/private/connection.h
@@ -0,0 +1,28 @@
+/*
+ * Created by yuuta on 7/19/22.
+ */
+
+#ifndef LIBAC_CONNECTION_H
+#define LIBAC_CONNECTION_H
+
+#include "wic.h"
+#include "libac.h"
+#include <stdbool.h>
+
+struct ac_result {
+ bool has_result;
+ int res;
+ ac_obj_t *obj;
+};
+
+struct ac_connection {
+ ac_connection_parameters_t parameters;
+ struct wic_inst inst;
+ int fd;
+ bool established;
+ /* Result from message handler.
+ * Message handling will happen at the same thread as ac_receive (lib wic). */
+ struct ac_result result;
+};
+
+#endif /* LIBAC_CONNECTION_H */
diff --git a/client/libacron/private/helpers.c b/client/libacron/private/helpers.c
new file mode 100644
index 0000000..4d4f3c8
--- /dev/null
+++ b/client/libacron/private/helpers.c
@@ -0,0 +1,64 @@
+/*
+ * Created by yuuta on 7/19/22.
+ */
+
+#include "helpers.h"
+#include "log.h"
+#include "common.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+int alloc(void *existing,
+ unsigned int count,
+ unsigned int bytes,
+ bool zero_out,
+ void **out) {
+ const unsigned int size = count * bytes;
+ void *ptr;
+ if (!(ptr = realloc(existing, size))) {
+ const int e = errno;
+ LOGEV("Cannot %sallocate %d bytes of memory: %s (%d).",
+ existing ? "re" : "",
+ size,
+ strerror2(e),
+ e);
+ return AC_E_MEMORY_ALLOCATION;
+ }
+ if (!existing && zero_out) {
+ memset(ptr, 0, size);
+ }
+ *out = ptr;
+ return AC_E_OK;
+}
+
+int strdup2(const char *str, char **out) {
+ char *str2;
+ if (!(str2 = strdup(str))) {
+ const int e = errno;
+ LOGEV("Cannot duplicate string '%s': %s (%d).",
+ str,
+ strerror2(e),
+ e);
+ return AC_E_MEMORY_ALLOCATION;
+ }
+ *out = str2;
+ return AC_E_OK;
+}
+
+static _Thread_local char err_buf[1024];
+
+char *strerror2(int errnum) {
+#ifdef WIN32
+ /* Win32 strerror(3) is thread-safe. */
+ return strerror(errnum);
+#else
+ int r;
+ if ((strerror_r(errnum, err_buf, sizeof(err_buf) / sizeof(char)))) {
+ snprintf(err_buf, 1024, "%d (%d)", errnum, r);
+ }
+ return err_buf;
+#endif
+} \ No newline at end of file
diff --git a/client/libacron/private/helpers.h b/client/libacron/private/helpers.h
new file mode 100644
index 0000000..20dfeec
--- /dev/null
+++ b/client/libacron/private/helpers.h
@@ -0,0 +1,22 @@
+/*
+ * Created by yuuta on 7/19/22.
+ */
+
+#ifndef LIBAC_HELPERS_H
+#define LIBAC_HELPERS_H
+
+#include <stdbool.h>
+
+#define SALLOC(s, out) alloc(NULL, 1, sizeof(s), true, (void **) out)
+
+int alloc(void *existing,
+ unsigned int count,
+ unsigned int bytes,
+ bool zero_out,
+ void **out);
+
+int strdup2(const char *str, char **out);
+
+char *strerror2(int errnum);
+
+#endif /* LIBAC_HELPERS_H */
diff --git a/client/libacron/private/log.c b/client/libacron/private/log.c
new file mode 100644
index 0000000..b5a246a
--- /dev/null
+++ b/client/libacron/private/log.c
@@ -0,0 +1,52 @@
+/*
+ * Created by yuuta on 1/1/22.
+ */
+
+#include "log.h"
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+
+void g_log(enum log_level level,
+ const char *file,
+ int line,
+ const char *format,
+ ...) {
+ if (config == NULL || config->out == NULL) {
+ return;
+ }
+ FILE *stream = config->out;
+ switch (level) {
+ case log_fetal:
+ fprintf(stream, "F");
+ break;
+ case log_error:
+ fprintf(stream, "E");
+ break;
+ case log_warn:
+ fprintf(stream, "W");
+ break;
+ case log_info:
+ fprintf(stream, "I");
+ break;
+ case log_debug:
+#ifdef DEBUG
+ fprintf(stream, "D");
+ break;
+#else
+ return;
+#endif
+ default:
+ fprintf(stderr, "Unknown log level: %d.\n", level);
+ assert(0);
+ }
+ fprintf(stream, "[%s:%d]: ",
+ file, line);
+ va_list list;
+ va_start(list, format);
+ vfprintf(stream, format, list);
+ va_end(list);
+ fprintf(stream, "\n");
+}
diff --git a/client/libacron/private/log.h b/client/libacron/private/log.h
new file mode 100644
index 0000000..7d41fbc
--- /dev/null
+++ b/client/libacron/private/log.h
@@ -0,0 +1,42 @@
+/*
+ * Created by yuuta on 1/1/22.
+ */
+
+#ifndef LOG_H
+#define LOG_H
+
+enum log_level {
+ log_fetal = 1,
+ log_error = 2,
+ log_warn = 3,
+ log_info = 4,
+ log_debug = 5
+};
+
+void g_log(enum log_level level,
+ const char *file,
+ int line,
+ const char *format,
+ ...);
+
+#define LOGF(X) g_log(log_fetal, __FUNCTION__, __LINE__, X)
+
+#define LOGFV(X, ...) g_log(log_fetal, __FUNCTION__, __LINE__, X, __VA_ARGS__)
+
+#define LOGE(X) g_log(log_error, __FUNCTION__, __LINE__, X)
+
+#define LOGEV(X, ...) g_log(log_error, __FUNCTION__, __LINE__, X, __VA_ARGS__)
+
+#define LOGW(X) g_log(log_warn, __FUNCTION__, __LINE__, X)
+
+#define LOGWV(X, ...) g_log(log_warn, __FUNCTION__, __LINE__, X, __VA_ARGS__)
+
+#define LOGI(X) g_log(log_info, __FUNCTION__, __LINE__, X)
+
+#define LOGIV(X, ...) g_log(log_info, __FUNCTION__, __LINE__, X, __VA_ARGS__)
+
+#define LOGD(X) g_log(log_debug, __FUNCTION__, __LINE__, X)
+
+#define LOGDV(X, ...) g_log(log_debug, __FUNCTION__, __LINE__, X, __VA_ARGS__)
+
+#endif /* LOG_H */
diff --git a/client/libacron/private/serializer.c b/client/libacron/private/serializer.c
new file mode 100644
index 0000000..c34c81f
--- /dev/null
+++ b/client/libacron/private/serializer.c
@@ -0,0 +1,572 @@
+/*
+ * Created by yuuta on 7/13/22.
+ */
+
+#include "libac.h"
+#include "config.h"
+#include "serializer.h"
+#include "helpers.h"
+#include "log.h"
+
+#include <json-c/json_object.h>
+#include <json-c/json_tokener.h>
+#include <json-c/json_util.h>
+#include <string.h>
+
+static int assert_type(const json_object *obj, const json_type type, const char *location) {
+ if (json_object_is_type(obj, type)) {
+ return AC_E_OK;
+ }
+ LOGEV("Cannot deserialize JSON at '%s': It should be a(n) %s, but it is a(n) %s.",
+ location,
+ json_type_to_name(type),
+ json_type_to_name(json_object_get_type(obj)));
+ return AC_E_INVALID_RESPONSE;
+}
+
+static int get_child(const json_object *obj,
+ const char *key,
+ const json_type type,
+ const bool mandatory,
+ json_object **out) {
+ if (!json_object_object_get_ex(obj, key, out)) {
+ if (!mandatory) {
+ *out = NULL;
+ return AC_E_OK;
+ }
+ LOGEV("Cannot deserialize JSON: A mandatory child '%s' is missing.",
+ key);
+ return AC_E_INVALID_RESPONSE;
+ }
+ return assert_type(*out, type, key);
+}
+
+static int deserialize_pre(const char *string,
+ unsigned int len,
+ json_object **out_json,
+ const char **out_type,
+ bool id_mandatory,
+ bool *out_has_id,
+ int32_t *out_id) {
+ AC_CHECK_INIT;
+ json_object *obj = NULL;
+ if (!(obj = json_tokener_parse_ex(config->tok,
+ string,
+ (int) len))) {
+ enum json_tokener_error err = json_tokener_get_error(config->tok);
+ if (err == json_tokener_continue) {
+ return AC_E_SERIALIZER_CONTINUE;
+ }
+ LOGEV("Cannot parse JSON response '%s': %s.",
+ string,
+ json_tokener_error_desc(err));
+ json_tokener_reset(config->tok);
+ return AC_E_INVALID_RESPONSE;
+ }
+ int r;
+ if ((r = assert_type(obj, json_type_object, "."))) {
+ json_object_put(obj);
+ return r;
+ }
+ json_object *arg;
+ if ((r = get_child(obj, "type", json_type_string, true, &arg))) {
+ json_object_put(obj);
+ return r;
+ }
+ *out_json = obj;
+ *out_type = json_object_get_string(arg);
+ if ((r = get_child(obj, "id", json_type_int, id_mandatory, &arg))) {
+ json_object_put(obj);
+ *out_has_id = false;
+ return r;
+ } else {
+ *out_id = json_object_get_int(arg);
+ /* If is_mandatory is false, get_child will return 0 even it arg == NULL. */
+ *out_has_id = arg;
+ }
+ return AC_E_OK;
+}
+
+static int deserialize_entity(const json_object *json, ac_entity_t *entity) {
+ int r;
+ json_object *arg;
+ if ((r = get_child(json, "name", json_type_string, true, &arg))) {
+ return r;
+ }
+ if ((r = strdup2(json_object_get_string(arg), &entity->name))) {
+ return r;
+ }
+
+ if ((r = get_child(json, "uuid", json_type_string, true, &arg))) {
+ return r;
+ }
+ if ((r = strdup2(json_object_get_string(arg), &entity->uuid))) {
+ return r;
+ }
+
+ if ((r = get_child(json, "world", json_type_string, false, &arg))) {
+ return r;
+ }
+ if (arg) {
+ const char *world_str = json_object_get_string(arg);
+ if (!strcmp("overworld", world_str)) {
+ entity->world = overworld;
+ } else if (!strcmp("nether", world_str)) {
+ entity->world = nether;
+ } else if (!strcmp("end", world_str)) {
+ entity->world = end;
+ } else {
+ LOGEV("Server returned an invalid world: %s.", world_str);
+ return r;
+ }
+ }
+
+ json_object *vec3d;
+ if ((r = get_child(json, "pos", json_type_object, true, &vec3d))) {
+ return r;
+ }
+ json_object *d;
+ if ((r = get_child(vec3d, "x", json_type_double, true, &d))) {
+ return r;
+ }
+ entity->pos.x = json_object_get_double(d);
+
+ if ((r = get_child(vec3d, "y", json_type_double, true, &d))) {
+ return r;
+ }
+ entity->pos.y = json_object_get_double(d);
+
+ if ((r = get_child(vec3d, "z", json_type_double, true, &d))) {
+ return r;
+ }
+ entity->pos.z = json_object_get_double(d);
+ return AC_E_OK;
+}
+
+static int deserialize_response(json_object *obj, const char *type_str, const int id, ac_response_t **out) {
+ int r;
+
+ if (!strcmp("ok", type_str)) {
+ ac_response_ok_t *ok;
+ if ((r = SALLOC(ac_response_t, &ok))) {
+ goto fail;
+ }
+ ok->type = AC_RESPONSE_OK;
+ ok->id = id;
+
+ *out = (ac_response_t *) ok;
+ } else if (!strcmp("error", type_str)) {
+ ac_response_error_t *error;
+ if ((r = SALLOC(ac_response_error_t, &error))) {
+ goto fail;
+ }
+ error->type = AC_RESPONSE_ERROR;
+ error->id = id;
+ json_object *arg;
+
+ if ((r = get_child(obj, "code", json_type_int, true, &arg))) {
+ ac_object_free((ac_obj_t *) error);
+ goto fail;
+ }
+ error->code = json_object_get_int(arg);
+
+ if ((r = get_child(obj, "message", json_type_string, true, &arg))) {
+ ac_object_free((ac_obj_t *) error);
+ goto fail;
+ }
+ if ((r = strdup2(json_object_get_string(arg), &error->message))) {
+ ac_object_free((ac_obj_t *) error);
+ goto fail;
+ }
+
+ *out = (ac_response_t *) error;
+ } else if (!strcmp("cmd_out", type_str)) {
+ ac_response_cmd_out_t *o;
+ if ((r = SALLOC(ac_response_cmd_out_t, &o))) {
+ goto fail;
+ }
+ o->type = AC_RESPONSE_CMD_OUT;
+ o->id = id;
+ json_object *arg;
+
+ if ((r = get_child(obj, "sender", json_type_string, true, &arg))) {
+ ac_object_free((ac_obj_t *) o);
+ goto fail;
+ }
+ if ((r = strdup2(json_object_get_string(arg), &o->sender))) {
+ ac_object_free((ac_obj_t *) o);
+ goto fail;
+ }
+
+ if ((r = get_child(obj, "out", json_type_string, true, &arg))) {
+ ac_object_free((ac_obj_t *) o);
+ goto fail;
+ }
+ if ((r = strdup2(json_object_get_string(arg), &o->out))) {
+ ac_object_free((ac_obj_t *) o);
+ goto fail;
+ }
+
+ *out = (ac_response_t *) o;
+ } else if (!strcmp("cmd_result", type_str)) {
+ ac_response_cmd_result_t *res;
+ if ((r = SALLOC(ac_response_cmd_result_t, &res))) {
+ goto fail;
+ }
+ res->type = AC_RESPONSE_CMD_RESULT;
+ res->id = id;
+ json_object *arg;
+
+ if ((r = get_child(obj, "result", json_type_int, true, &arg))) {
+ ac_object_free((ac_obj_t *) res);
+ goto fail;
+ }
+ res->result = json_object_get_int(arg);
+
+ if ((r = get_child(obj, "success", json_type_boolean, true, &arg))) {
+ ac_object_free((ac_obj_t *) res);
+ goto fail;
+ }
+ res->success = json_object_get_boolean(arg);
+
+ *out = (ac_response_t *) res;
+ } else {
+ LOGEV("Invalid response type: '%s'.", type_str);
+ r = AC_E_INVALID_RESPONSE;
+ goto fail;
+ }
+ return AC_E_OK;
+ fail:
+ if (obj) {
+ json_object_put(obj);
+ }
+ return r;
+}
+
+static int deserialize_event(json_object *obj, const char *type_str, ac_event_t **out) {
+ LOGDV("Deserializing event type '%s'", type_str);
+ int r;
+
+ if (!strcmp("join", type_str)) {
+ ac_event_player_join_t *join;
+ if ((r = SALLOC(ac_event_player_join_t, &join))) {
+ goto fail;
+ }
+ join->type = AC_EVENT_PLAYER_JOIN;
+
+ json_object *arg;
+ if ((r = get_child(obj, "player", json_type_object, true, &arg))) {
+ ac_object_free((ac_obj_t *) join);
+ goto fail;
+ }
+ if ((r = deserialize_entity(arg, &join->player))) {
+ ac_object_free((ac_obj_t *) join);
+ goto fail;
+ }
+
+ *out = (ac_event_t *) join;
+ } else if (!strcmp("disconnect", type_str)) {
+ ac_event_player_disconnect_t *disconnect;
+ if ((r = SALLOC(ac_event_player_disconnect_t, &disconnect))) {
+ goto fail;
+ }
+ disconnect->type = AC_EVENT_PLAYER_DISCONNECT;
+
+ json_object *arg;
+ if ((r = get_child(obj, "player", json_type_object, true, &arg))) {
+ ac_object_free((ac_obj_t *) disconnect);
+ goto fail;
+ }
+ if ((r = deserialize_entity(arg, &disconnect->player))) {
+ ac_object_free((ac_obj_t *) disconnect);
+ goto fail;
+ }
+
+ if ((r = get_child(obj, "reason", json_type_string, true, &arg))) {
+ ac_object_free((ac_obj_t *) disconnect);
+ goto fail;
+ }
+ if ((r = strdup2(json_object_get_string(arg), &disconnect->reason))) {
+ ac_object_free((ac_obj_t *) disconnect);
+ goto fail;
+ }
+
+ *out = (ac_event_t *) disconnect;
+ } else if (!strcmp("message", type_str)) {
+ ac_event_player_message_t *message;
+ if ((r = SALLOC(ac_event_player_message_t, &message))) {
+ goto fail;
+ }
+ message->type = AC_EVENT_PLAYER_MESSAGE;
+
+ json_object *arg;
+ if ((r = get_child(obj, "player", json_type_object, true, &arg))) {
+ ac_object_free((ac_obj_t *) message);
+ goto fail;
+ }
+ if ((r = deserialize_entity(arg, &message->player))) {
+ ac_object_free((ac_obj_t *) message);
+ goto fail;
+ }
+
+ if ((r = get_child(obj, "text", json_type_string, true, &arg))) {
+ ac_object_free((ac_obj_t *) message);
+ goto fail;
+ }
+ if ((r = strdup2(json_object_get_string(arg), &message->text))) {
+ ac_object_free((ac_obj_t *) message);
+ goto fail;
+ }
+
+ *out = (ac_event_t *) message;
+ } else if (!strcmp("death", type_str)) {
+ ac_event_entity_death_t *death;
+ if ((r = SALLOC(ac_event_entity_death_t, &death))) {
+ goto fail;
+ }
+ death->type = AC_EVENT_ENTITY_DEATH;
+
+ json_object *arg;
+ if ((r = get_child(obj, "entity", json_type_object, true, &arg))) {
+ ac_object_free((ac_obj_t *) death);
+ goto fail;
+ }
+ if ((r = deserialize_entity(arg, &death->entity))) {
+ ac_object_free((ac_obj_t *) death);
+ goto fail;
+ }
+
+ if ((r = get_child(obj, "message", json_type_string, true, &arg))) {
+ ac_object_free((ac_obj_t *) death);
+ goto fail;
+ }
+ if ((r = strdup2(json_object_get_string(arg), &death->message))) {
+ ac_object_free((ac_obj_t *) death);
+ goto fail;
+ }
+
+ *out = (ac_event_t *) death;
+ } else if (!strcmp("lagging", type_str)) {
+ ac_event_lagging_t *lagging;
+ if ((r = SALLOC(ac_event_lagging_t, &lagging))) {
+ goto fail;
+ }
+ lagging->type = AC_EVENT_LAGGING;
+
+ json_object *arg;
+ if ((r = get_child(obj, "ms", json_type_int, true, &arg))) {
+ goto fail;
+ }
+ lagging->ms = json_object_get_int64(arg);
+
+ if ((r = get_child(obj, "ticks", json_type_int, true, &arg))) {
+ goto fail;
+ }
+ lagging->ticks = json_object_get_int64(arg);
+
+ *out = (ac_event_t *) lagging;
+ } else {
+ LOGEV("Invalid event type: '%s'.", type_str);
+ r = AC_E_INVALID_RESPONSE;
+ goto fail;
+ }
+
+ return AC_E_OK;
+ fail:
+ if (obj) {
+ json_object_put(obj);
+ }
+ return r;
+}
+
+int deserialize(const char *string, unsigned int len, ac_obj_t **out) {
+ AC_CHECK_INIT;
+ json_object *obj;
+ const char *type_str;
+ int id;
+ bool has_id;
+ int r;
+
+ if ((r = deserialize_pre(string,
+ len,
+ &obj,
+ &type_str,
+ false,
+ &has_id,
+ &id))) {
+ return r;
+ }
+
+ if (has_id) {
+ return deserialize_response(obj, type_str, id, (ac_response_t **) out);
+ } else {
+ return deserialize_event(obj, type_str, (ac_event_t **) out);
+ }
+}
+
+static int json_add(json_object *obj, const char *key, json_object *val) {
+ if (!val) {
+ LOGEV("Cannot initialize JSON child '%s'.", key);
+ return AC_E_MEMORY_ALLOCATION;
+ }
+ if ((json_object_object_add(obj, key, val))) {
+ LOGEV("Cannot put '%s' to the JSON object.", key);
+ json_object_put(val);
+ return AC_E_MEMORY_ALLOCATION;
+ }
+ return AC_E_OK;
+}
+
+static int serialize_config(const ac_config_t *c, json_object *obj) {
+ int r;
+ if (c->pos) {
+ ac_vec3d_t p = *c->pos;
+ json_object *pos;
+ if (!(pos = json_object_new_object())) {
+ LOGE("Cannot initialize a new JSON object.");
+ return AC_E_MEMORY_ALLOCATION;
+ }
+ if ((r = json_add(pos, "x", json_object_new_double(p.x)))) {
+ json_object_put(pos);
+ return r;
+ }
+ if ((r = json_add(pos, "y", json_object_new_double(p.y)))) {
+ json_object_put(pos);
+ return r;
+ }
+ if ((r = json_add(pos, "z", json_object_new_double(p.z)))) {
+ json_object_put(pos);
+ return r;
+ }
+ if ((r = json_add(obj, "pos", pos))) {
+ return r;
+ }
+ }
+ if (c->world) {
+ enum ac_world world = *c->world;
+ const char *str;
+ switch (world) {
+ case overworld:
+ str = "overworld";
+ break;
+ case nether:
+ str = "nether";
+ break;
+ case end:
+ str = "end";
+ break;
+ default:
+ LOGE("Invalid world supplied.");
+ return AC_E_INVALID_REQUEST;
+ }
+ if ((r = json_add(obj, "world", json_object_new_string(str)))) {
+ return r;
+ }
+ }
+ if (c->name) {
+ if ((r = json_add(obj, "name", json_object_new_string(c->name)))) {
+ return r;
+ }
+ }
+ if (c->rot) {
+ ac_vec2f_t t = *c->rot;
+ json_object *rot;
+ if (!(rot = json_object_new_object())) {
+ LOGE("Cannot initialize a new JSON object.");
+ return AC_E_MEMORY_ALLOCATION;
+ }
+ if ((r = json_add(rot, "x", json_object_new_double(t.x)))) {
+ json_object_put(rot);
+ return r;
+ }
+ if ((r = json_add(rot, "y", json_object_new_double(t.y)))) {
+ json_object_put(rot);
+ return r;
+ }
+ if ((r = json_add(obj, "rot", rot))) {
+ return r;
+ }
+ }
+ return AC_E_OK;
+}
+
+int serialize_request(const ac_request_t *req, json_object **out) {
+ AC_CHECK_INIT;
+ if ((req->type & 192) != AC_TYPE_REQUEST) {
+ LOGE("Invalid argument to serialize_request. It should be a request.");
+ return AC_E_INVALID_REQUEST;
+ }
+ json_object *obj;
+ if (!(obj = json_object_new_object())) {
+ LOGE("Cannot initialize a new JSON object.");
+ return AC_E_MEMORY_ALLOCATION;
+ }
+ int r;
+ if ((r = json_add(obj, "id", json_object_new_int(req->id)))) {
+ goto fail;
+ }
+ switch (req->type) {
+ case AC_REQUEST_CMD: {
+ if ((r = json_add(obj, "type", json_object_new_string("cmd")))) {
+ goto fail;
+ }
+ ac_request_cmd_t *cmd = (ac_request_cmd_t *) req;
+ if ((r = json_add(obj, "cmd", json_object_new_string(cmd->cmd)))) {
+ goto fail;
+ }
+ if (cmd->config) {
+ json_object *c;
+ if (!(c = json_object_new_object())) {
+ LOGE("Cannot initialize a new JSON object.");
+ goto fail;
+ }
+ if ((r = serialize_config(cmd->config, c))) {
+ json_object_put(c);
+ goto fail;
+ }
+ if ((r = json_add(obj, "config", c))) {
+ goto fail;
+ }
+ }
+ break;
+ }
+ case AC_REQUEST_SET_CONFIG: {
+ if ((r = json_add(obj, "set_config", json_object_new_string("cmd")))) {
+ goto fail;
+ }
+ ac_request_set_config_t *set_config = (ac_request_set_config_t *) req;
+ json_object *c;
+ if (!(c = json_object_new_object())) {
+ LOGE("Cannot initialize a new JSON object.");
+ goto fail;
+ }
+ if ((r = serialize_config(&set_config->config, c))) {
+ json_object_put(c);
+ goto fail;
+ }
+ if ((r = json_add(obj, "config", c))) {
+ goto fail;
+ }
+ break;
+ }
+ default: {
+ LOGE("Invalid request supplied.");
+ r = AC_E_INVALID_REQUEST;
+ goto fail;
+ }
+ }
+ *out = obj;
+ return AC_E_OK;
+ fail:
+ {
+ if (obj) {
+ json_object_put(obj);
+ }
+ return r;
+ }
+}
+
+int serializer_finalize(void) {
+ AC_CHECK_INIT;
+ json_tokener_reset(config->tok);
+}
diff --git a/client/libacron/private/serializer.h b/client/libacron/private/serializer.h
new file mode 100644
index 0000000..1b9a38b
--- /dev/null
+++ b/client/libacron/private/serializer.h
@@ -0,0 +1,18 @@
+/*
+ * Created by yuuta on 7/19/22.
+ */
+
+#ifndef LIBAC_SERIALIZER_H
+#define LIBAC_SERIALIZER_H
+
+/* An internal error code indicating that more data is required.
+ * Should not conflict with other error codes. */
+#define AC_E_SERIALIZER_CONTINUE -1
+
+int deserialize(const char *string, unsigned int len, ac_obj_t **out);
+
+int serializer_finalize(void);
+
+int serialize_request(const ac_request_t *req, json_object **out);
+
+#endif /* LIBAC_SERIALIZER_H */