summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/flags.c155
-rw-r--r--src/flags.h17
-rw-r--r--src/json/json.h7
-rw-r--r--src/json/print.c173
-rw-r--r--src/json/read.c634
-rw-r--r--src/main.c54
-rw-r--r--src/nbt/nbt.h7
-rw-r--r--src/nbt/print.c125
-rw-r--r--src/nbt/read.c (renamed from src/tag_read.c)52
-rw-r--r--src/stream.c95
-rw-r--r--src/stream.h21
-rw-r--r--src/tag.c66
-rw-r--r--src/tag.h14
-rw-r--r--src/tag_print.c101
14 files changed, 1349 insertions, 172 deletions
diff --git a/src/flags.c b/src/flags.c
new file mode 100644
index 0000000..58311ac
--- /dev/null
+++ b/src/flags.c
@@ -0,0 +1,155 @@
+#include "flags.h"
+#include "lib.h"
+#include "stream.h"
+
+#include <strings.h>
+
+static format_t get_file_extension(char *path) {
+ char *filename = strrchr(path, '/');
+ if (filename == NULL)
+ return NBT;
+ char *extension = strrchr(filename, '.');
+ if (extension == NULL)
+ return NBT;
+ else if (strcasecmp(extension, "json") == 0)
+ return JSON;
+ else if (strcasecmp(extension, "snbt") == 0)
+ return SNBT;
+ return NBT;
+}
+
+void parse_long_arg(flags_t *flags, char *arg) {
+
+ char *key = arg + 2;
+ char *value = strchr(key, '=');
+ if (value != NULL) {
+ *value = '\0';
+ value++;
+ }
+
+ if (strcmp(key, "help") == 0) {
+ flags->help = true;
+ return;
+ }
+
+ else if (strcmp(key, "version") == 0) {
+ flags->version = true;
+ return;
+ }
+
+ else if (strcmp(key, "in") == 0) {
+ if (value == NULL)
+ error_and_die("--in requires a value\n");
+ flags->in = stream_open(value, "r");
+ if (flags->__set_fin == false)
+ flags->fin = get_file_extension(value);
+ }
+
+ else if (strcmp(key, "out") == 0) {
+ if (value == NULL)
+ error_and_die("--out requires a value\n");
+ flags->out = stream_open(value, "w");
+ if (flags->__set_fout == false)
+ flags->fout = get_file_extension(value);
+ }
+
+ else {
+ error_and_die("invalid argument '--%s'\n", key);
+ }
+
+}
+
+void parse_short_args(flags_t *flags, char *arg, int len) {
+ for (int i = 1; i < len; i++) {
+ char c = arg[i];
+ switch(c) {
+ case 'j':
+ flags->fin = JSON;
+ flags->__set_fin = true;
+ break;
+ case 'J':
+ flags->fout = JSON;
+ flags->__set_fout = true;
+ break;
+ case 's':
+ flags->fin = SNBT;
+ break;
+ case 'S':
+ flags->fout = SNBT;
+ break;
+ case 'n':
+ flags->fin = NBT;
+ break;
+ case 'N':
+ flags->fout = NBT;
+ break;
+ case 'h':
+ flags->help = true;
+ break;
+ case 'v':
+ flags->version = true;
+ break;
+ default:
+ error_and_die("invalid argument: '-%c'\n", c);
+ }
+ }
+}
+
+void parse_arg(flags_t *flags, char *arg, int len) {
+ if (len < 2) {
+ error_and_die("invalid argument: '%s'\n", arg);
+ } else if (arg[1] == '-') {
+ parse_long_arg(flags, arg);
+ } else {
+ parse_short_args(flags, arg, len);
+ }
+}
+
+void parse_flags(flags_t *flags, int argc, char **argv) {
+
+ flags->__set_fin = false;
+ flags->__set_fout = false;
+ flags->help = false;
+ flags->version = false;
+ flags->in.__file = stdin;
+ flags->in.__alloc = false;
+ flags->out.__file = stdout;
+ flags->out.__alloc = false;
+ flags->fin = NBT;
+ flags->fout = NBT;
+
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ char *arg = argv[i];
+ int len = strlen(arg);
+
+ if (len == 2 && arg[0] == '-' && arg[1] == '-') {
+ break;
+ } else if (arg[0] != '-') {
+ i--;
+ break;
+ } else {
+ parse_arg(flags, arg, len);
+ }
+ }
+
+ if (i + 1 < argc) {
+ char *arg = argv[i + 1];
+ flags->in = stream_open(arg, "r");
+ if (flags->__set_fin == false)
+ flags->fin = get_file_extension(arg);
+ }
+
+ if (i + 2 < argc) {
+ char *arg = argv[i + 2];
+ flags->out = stream_open(arg, "w");
+ if (flags->__set_fout == false)
+ flags->fout = get_file_extension(arg);
+ }
+
+ if (i + 3 < argc) {
+ error_and_die("too many arguments passed\n");
+ }
+
+}
diff --git a/src/flags.h b/src/flags.h
new file mode 100644
index 0000000..a14580b
--- /dev/null
+++ b/src/flags.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "tag.h"
+#include "stream.h"
+
+typedef struct {
+ stream_t in;
+ stream_t out;
+ format_t fin;
+ format_t fout;
+ bool help;
+ bool version;
+ bool __set_fin;
+ bool __set_fout;
+} flags_t;
+
+void parse_flags(flags_t *flags, int argc, char **argv);
diff --git a/src/json/json.h b/src/json/json.h
new file mode 100644
index 0000000..73b4f52
--- /dev/null
+++ b/src/json/json.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "../tag.h"
+#include "../stream.h"
+
+bool json_read(tag_t *tag, const stream_t *stream);
+bool json_print(const tag_t *tag, const stream_t *stream);
diff --git a/src/json/print.c b/src/json/print.c
new file mode 100644
index 0000000..e8b6996
--- /dev/null
+++ b/src/json/print.c
@@ -0,0 +1,173 @@
+#include "json.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+static char buf[1024];
+
+__attribute__((format(printf, 3, 4)))
+static bool printi(const stream_t *stream, int depth, const char *format, ...) {
+ for (int i = 0; i < depth; i++)
+ if (stream_write(stream, "\t", 1) == false)
+ return false;
+ va_list list;
+ va_start(list, format);
+ int len;
+ if ((len = vsnprintf(buf, 1024, format, list)) < 0)
+ return false;
+ if (stream_write(stream, buf, len) == false)
+ return false;
+ return true;
+}
+
+static bool json_print_impl(const tag_t *tag, const stream_t *stream, int depth);
+
+static bool json_print_byte_array(const tagdata_t *data, const stream_t *stream) {
+ if (printi(stream, 0, "[") == false)
+ return false;
+ for (int32_t i = 0; i < data->b_arr.size; i++) {
+ if (i != 0)
+ if (printi(stream, 0, ",") == false)
+ return false;
+ if (printi(stream, 0, "%hhd", data->b_arr.data[i]) == false)
+ return false;
+ }
+ if (printi(stream, 0, "]") == false)
+ return false;
+ return true;
+}
+
+static bool json_print_int_array(const tagdata_t *data, const stream_t *stream) {
+ if (printi(stream, 0, "[") == false)
+ return false;
+ for (int32_t i = 0; i < data->i_arr.size; i++) {
+ if (i != 0)
+ if (printi(stream, 0, ",") == false)
+ return false;
+ if (printi(stream, 0, "%d", data->i_arr.data[i]) == false)
+ return false;
+ }
+ if (printi(stream, 0, "]") == false)
+ return false;
+ return true;
+}
+
+static bool json_print_long_array(const tagdata_t *data, const stream_t *stream) {
+ if (printi(stream, 0, "[") == false)
+ return false;
+ for (int32_t i = 0; i < data->l_arr.size; i++) {
+ if (i != 0)
+ if (printi(stream, 0, ",") == false)
+ return false;
+ if (printi(stream, 0, "%ld", data->l_arr.data[i]) == false)
+ return false;
+ }
+ if (printi(stream, 0, "]") == false)
+ return false;
+ return true;
+}
+
+static bool json_print_string(const tagdata_t *data, const stream_t *stream) {
+ if (data->string.size > 1) {
+ if (printi(stream, 0, "\"%.*s\"", data->string.size, data->string.data) == false)
+ return false;
+ } else {
+ if (printi(stream, 0, "\"\"") == false)
+ return false;
+ }
+ return true;
+}
+
+static bool json_print_compound(const tagdata_t *data, const stream_t *stream, int depth) {
+ if (printi(stream, 0, "{\n") == false)
+ return false;
+ for (int32_t i = 0; i < data->compound.size; i++) {
+ if (i != 0 && printi(stream, 0, ",\n") == false)
+ return false;
+ if (json_print_impl(&data->compound.tags[i], stream, depth + 1) == false)
+ return false;
+ }
+ if (printi(stream, 0, "\n") == false || printi(stream, depth, "}") == false)
+ return false;
+ return true;
+}
+
+static bool json_print_list(const tagdata_t *data, const stream_t *stream, int depth) {
+ if (printi(stream, 0, "[\n") == false)
+ return false;
+ for (int32_t i = 0; i < data->list.size; i++) {
+ if (i != 0 && printi(stream, 0, ",\n") == false)
+ return false;
+ if (json_print_impl(&data->list.tags[i], stream, depth + 1) == false)
+ return false;
+ }
+ if (printi(stream, 0, "\n") == false || printi(stream, depth, "]") == false)
+ return false;
+ return true;
+}
+
+static bool json_print_data(const tag_t *tag, const stream_t *stream, int depth) {
+
+ bool ok = true;
+
+ switch (tag->type) {
+ case TAG_BYTE:
+ ok = printi(stream, 0, "%hhd", tag->data.b);
+ break;
+ case TAG_SHORT:
+ ok = printi(stream, 0, "%hd", tag->data.s);
+ break;
+ case TAG_INT:
+ ok = printi(stream, 0, "%d", tag->data.i);
+ break;
+ case TAG_LONG:
+ ok = printi(stream, 0, "%ld", tag->data.l);
+ break;
+ case TAG_FLOAT:
+ ok = printi(stream, 0, "%f", tag->data.f);
+ break;
+ case TAG_DOUBLE:
+ ok = printi(stream, 0, "%lf", tag->data.d);
+ break;
+ case TAG_BYTE_ARRAY:
+ ok = json_print_byte_array(&tag->data, stream);
+ break;
+ case TAG_STRING:
+ ok = json_print_string(&tag->data, stream);
+ break;
+ case TAG_LIST:
+ ok = json_print_list(&tag->data, stream, depth);
+ break;
+ case TAG_COMPOUND:
+ ok = json_print_compound(&tag->data, stream, depth);
+ break;
+ case TAG_INT_ARRAY:
+ ok = json_print_int_array(&tag->data, stream);
+ break;
+ case TAG_LONG_ARRAY:
+ ok = json_print_long_array(&tag->data, stream);
+ break;
+ case TAG_END:
+ break;
+ }
+
+ return ok;
+}
+
+static bool json_print_impl(const tag_t *tag, const stream_t *stream, int depth) {
+ if (tag->name_len > 0) {
+ printi(stream, depth, "\"%.*s\":\t", tag->name_len, tag->name);
+ } else {
+ for (int i = 0; i < depth; i++)
+ printi(stream, 0, "\t");
+ }
+ return json_print_data(tag, stream, depth);
+}
+
+bool json_print(const tag_t *tag, const stream_t *stream) {
+ if (json_print_impl(tag, stream, 0) == false)
+ return false;
+ if (stream_write(stream, "\n", 1) == false)
+ return false;
+ return true;
+}
diff --git a/src/json/read.c b/src/json/read.c
new file mode 100644
index 0000000..9b75597
--- /dev/null
+++ b/src/json/read.c
@@ -0,0 +1,634 @@
+#include "json.h"
+#include "../lib.h"
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char ret = '\0';
+
+typedef enum {
+ TOK_LBRACE,
+ TOK_RBRACE,
+ TOK_LBRACK,
+ TOK_RBRACK,
+ TOK_COLON,
+ TOK_COMMA,
+ TOK_STRING,
+ TOK_NUMBER,
+ TOK_DOUBLE,
+ TOK_BOOL,
+ TOK_NULL
+} tokentype_t ;
+
+typedef union {
+ bool b;
+ int64_t number;
+ double decimal;
+ struct {
+ uint16_t len;
+ char *data;
+ } string;
+} tokendata_t;
+
+typedef struct {
+ tokentype_t type;
+ tokendata_t data;
+} token_t;
+
+static void json_token_free(token_t *token) {
+ if (token->type == TOK_STRING)
+ free(token->data.string.data);
+}
+
+static bool json_parse_unicode(char buf[4], int *read, const stream_t *stream) {
+ char temp[5];
+ temp[4] = '\0';
+
+ if (stream_read(stream, temp, 4) == false)
+ return false;
+
+ uint16_t code_point;
+ char *end = NULL;
+
+ code_point = strtol(temp, &end, 16);
+
+ if (end != NULL)
+ return false;
+
+ int lead1 = 0b00000000;
+ int lead2 = 0b11000000;
+ int lead3 = 0b11100000;
+ int cont = 0b10000000;
+ int contmask = 0b00111111;
+
+ if (code_point < 0x0080) {
+ buf[0] = ((code_point >> 0)) | lead1;
+ *read = 1;
+ } else if (code_point < 0x0800) {
+ buf[0] = ((code_point >> 6)) | lead2;
+ buf[1] = ((code_point >> 0) & contmask) | cont;
+ *read = 2;
+ } else {
+ buf[0] = ((code_point >> 12)) | lead3;
+ buf[1] = ((code_point >> 6) & contmask) | cont;
+ buf[2] = ((code_point >> 0) & contmask) | cont;
+ *read = 3;
+ }
+
+ return true;
+}
+
+static bool json_parse_escape(char buf[4], int *read, const stream_t *stream) {
+ char n;
+
+ char *c = &buf[0];
+ *read = 1;
+
+ if (stream_read(stream, &n, 1) == false)
+ return false;
+
+ switch (n) {
+ case '"':
+ case '\\':
+ case '/':
+ *c = n;
+ return true;
+ case 'b':
+ *c = '\b';
+ return true;
+ case 'f':
+ *c = '\f';
+ return true;
+ case 'n':
+ *c = '\n';
+ return true;
+ case 'r':
+ *c = '\r';
+ return true;
+ case 't':
+ *c = '\t';
+ return true;
+ case 'u': {
+ return json_parse_unicode(buf, read, stream);
+ default:
+ // invalid escape
+ return false;
+ };
+ }
+}
+
+static bool json_parse_string(tokendata_t *token, const stream_t *stream) {
+
+ int capacity = 8;
+ int len = 0;
+ char *buf = xalloc(capacity * sizeof(char));
+
+ while (1) {
+
+ char tmp[4];
+ int read = 1;
+
+ if (stream_read(stream, tmp, 1) == false) {
+ free(buf);
+ return false;
+ }
+
+ uint8_t c = tmp[0];
+
+ if (c == '"')
+ break;
+
+ // non printable ascii character
+ if (c < 32 || c > 127) {
+ free(buf);
+ return false;
+ }
+ // an escape, dont push to buffer, get next char
+ if (c == '\\' && json_parse_escape(tmp, &read, stream) == false) {
+ free(buf);
+ return false;
+ }
+
+ if (len + read >= capacity) {
+ capacity *= 2;
+ buf = xrealloc(buf, capacity);
+ }
+
+ memcpy(buf + len, tmp, read);
+ len += read;
+
+ }
+
+ token->string.data = xalloc(len * sizeof(char));
+ token->string.len = len;
+ memcpy(token->string.data, buf, len);
+ free(buf);
+
+ return true;
+}
+
+static bool json_parse_ident(token_t *token, const stream_t *stream, char first) {
+
+ char buf[4];
+ buf[3] = '\0';
+
+ if (stream_read(stream, buf, 3) == false)
+ return false;
+
+ if (first == 't' && strcmp(buf, "rue") == 0) {
+ token->type = TOK_BOOL;
+ token->data.b = true;
+ } else if (first == 'f' && strcmp(buf, "als") == 0) {
+ if (stream_read(stream, buf, 1) == false)
+ return false;
+ if (*buf != 'e')
+ return false;
+ token->type = TOK_BOOL;
+ token->data.b = false;
+ } else if (first == 'n' && strcmp(buf, "ull") == 0) {
+ token->type = TOK_NULL;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static void push_char(char **buf, int *len, int *cap, char c) {
+ if (*len == *cap) {
+ *cap *= *cap * 2;
+ *buf = xrealloc(*buf, *cap * sizeof(char));
+ }
+ (*buf)[(*len)++] = c;
+}
+
+static bool json_parse_number(token_t *token, const stream_t *stream, char first) {
+
+ int capacity = 8;
+ int len = 0;
+ char *buf = xalloc(capacity * sizeof(char));
+ bool isdec = false;
+ bool isneg = false;
+
+ char c = first;
+
+ // PARSE DIGITS AND NEGATIVITY
+
+ while (1) {
+ if (c == '\0' && stream_read(stream, &c, 1) == false) {
+ free(buf);
+ return false;
+ }
+
+ if (c == '-' && isneg) {
+ // cannot negate twice
+ free(buf);
+ return false;
+ } else if (c == '-') {
+ isneg = true;
+ c = '\0';
+ } else if (c == '0' && len == 0) {
+ // string starting with 0 cannot not have other digits
+ push_char(&buf, &len, &capacity, c);
+ c = '\0';
+ break;
+ } else if (c >= '0' && c <= '9') {
+ push_char(&buf, &len, &capacity, c);
+ c = '\0';
+ } else if (len == 0) {
+ // invalid start of digits
+ free(buf);
+ return false;
+ } else {
+ // end of starting digits
+ break;
+ }
+ }
+
+ // SET NEXT CHAR C IF NOT READ YET
+
+ if (c == '\0' && stream_read(stream, &c, 1) == false) {
+ free(buf);
+ return false;
+ }
+
+ // THERE IS A DECIMAL
+ // READ STREAM OF DIGITS
+
+ if (c == '.') {
+ isdec = true;
+ push_char(&buf, &len, &capacity, c);
+ int declen = 0;
+ while (1) {
+ if (stream_read(stream, &c, 1) == false) {
+ free(buf);
+ return false;
+ }
+ if (c >= '0' && c <= '9') {
+ push_char(&buf, &len, &capacity, c);
+ declen++;
+ } else if (declen == 0) {
+ // invalid decimal
+ free(buf);
+ return false;
+ } else {
+ // end of decimal
+ break;
+ }
+ }
+ }
+
+ // PARSE EXPONENT
+ if (c == 'e' || c == 'E') {
+ isdec = true;
+ push_char(&buf, &len, &capacity, 'E');
+
+ int explen = 0; // the exponent len
+
+ if (stream_read(stream, &c, 1) == false) {
+ free(buf);
+ return false;
+ }
+
+ if (c == '+' || c == '-') {
+ push_char(&buf, &len, &capacity, c);
+ c = '\0';
+ }
+
+ while (1) {
+ if (c == '\0' && stream_read(stream, &c, 1) == false) {
+ free(buf);
+ return false;
+ }
+
+ if (c >= '0' && c <= '9') {
+ push_char(&buf, &len, &capacity, c);
+ explen++;
+ c = '\0';
+ } else if (explen == 0) {
+ // invalid exponent
+ free(buf);
+ return false;
+ } else {
+ break;
+ }
+ }
+
+ }
+
+ char *end = NULL;
+ push_char(&buf, &len, &capacity, '\0');
+
+ if (isdec) {
+ token->type = TOK_DOUBLE;
+ token->data.decimal = strtod(buf, &end);
+ } else {
+ token->type = TOK_NUMBER;
+ token->data.number = strtol(buf, &end, 10);
+ }
+
+ if (end != NULL && *end != 0)
+ return false;
+
+ free(buf);
+
+ ret = c;
+ return true;
+}
+
+static bool json_next_token(token_t *token, const stream_t *stream) {
+
+ memset(token, 0, sizeof(token_t));
+
+ char c;
+
+retry:
+
+ if (ret != '\0') {
+ c = ret;
+ ret = '\0';
+ } else if (stream_read(stream, &c, 1) == false) {
+ return false;
+ }
+
+ bool ok = true;
+
+ switch (c) {
+ case '{':
+ token->type = TOK_LBRACE;
+ break;
+ case '}':
+ token->type = TOK_RBRACE;
+ break;
+ case '[':
+ token->type = TOK_LBRACK;
+ break;
+ case ']':
+ token->type = TOK_RBRACK;
+ break;
+ case ':':
+ token->type = TOK_COLON;
+ break;
+ case ',':
+ token->type = TOK_COMMA;
+ break;
+ case '"':
+ token->type = TOK_STRING;
+ ok = json_parse_string(&token->data, stream);
+ break;
+ case 't':
+ case 'f':
+ case 'n':
+ // parse null or bool
+ ok = json_parse_ident(token, stream, c);
+ break;
+ case ' ':
+ case '\n':
+ case '\t':
+ case '\r':
+ goto retry;
+ default:
+ if (isdigit(c) || c == '-') {
+ // parse number
+ ok = json_parse_number(token, stream, c);
+ } else {
+ // disallowed symbol
+ ok = false;
+ }
+ break;
+ }
+
+ return ok;
+}
+
+static bool json_get_list_type(tagtype_t *type, const tag_t *tags, int len) {
+ if (len < 1) {
+ *type = TAG_END;
+ return true;
+ }
+
+ *type = tags[0].type;
+
+ for (int i = 0; i < len; i++)
+ if (tags[i].type != *type)
+ return false;
+
+ return true;
+}
+
+static bool json_read_value(tag_t *tag, const stream_t *stream, token_t *first);
+
+static bool json_read_list(tagdata_t *data, const stream_t *stream) {
+
+ token_t next = {0};
+ if (json_next_token(&next, stream) == false) {
+ json_token_free(&next);
+ return false;
+ }
+
+ token_t *ret = &next;
+
+ if (next.type == TOK_RBRACK) {
+ data->list.tags = NULL;
+ data->list.size = 0;
+ data->list.type = TAG_END;
+ return true;
+ }
+
+ int capacity = 8;
+ int len = 0;
+ tag_t *tags = xalloc(capacity * sizeof(tag_t));
+
+ while (1) {
+
+ tag_t value;
+ value.name = "";
+ value.name_len = 0;
+
+ if (json_read_value(&value, stream, ret) == false) {
+ free(tags);
+ return false;
+ }
+
+ ret = NULL;
+
+ if (len == capacity) {
+ capacity *= 2;
+ tags = xrealloc(tags, capacity * sizeof(tag_t));
+ }
+
+ tags[len++] = value;
+
+ if (json_next_token(&next, stream) == false) {
+ free(tags);
+ json_token_free(&next);
+ return false;
+ }
+
+ if (next.type == TOK_COMMA) {
+ continue;
+ } else if (next.type == TOK_RBRACK) {
+ break;
+ } else {
+ free(tags);
+ json_token_free(&next);
+ return false;
+ }
+
+ }
+
+ tagtype_t type;
+ if (json_get_list_type(&type, tags, len) == false) {
+ free(tags);
+ return false;
+ }
+
+ data->list.type = type;
+ data->list.size = len;
+ data->list.tags = xalloc(len * sizeof(tag_t));
+ memcpy(data->list.tags, tags, len * sizeof(tag_t));
+ free(tags);
+
+ return true;
+
+}
+
+static bool json_read_compound(tagdata_t *data, const stream_t *stream) {
+
+ token_t next = {0};
+ if (json_next_token(&next, stream) == false) {
+ json_token_free(&next);
+ return false;
+ }
+
+ if (next.type == TOK_RBRACE) {
+ data->compound.tags = NULL;
+ data->compound.size = 0;
+ return true;
+ }
+
+ int capacity = 8;
+ int len = 0;
+ tag_t *tags = xalloc(capacity * sizeof(tag_t));
+
+ while (1) {
+
+ if (next.type != TOK_STRING) {
+ free(tags);
+ json_token_free(&next);
+ return false;
+ }
+
+ char *name = next.data.string.data;
+ int name_len = next.data.string.len;
+
+ if (json_next_token(&next, stream) == false || next.type != TOK_COLON) {
+ free(tags);
+ free(name);
+ return false;
+ }
+
+ tag_t value;
+ if (json_read_value(&value, stream, NULL) == false) {
+ free(tags);
+ free(name);
+ return false;
+ }
+
+ value.name = name;
+ value.name_len = name_len;
+
+ if (len == capacity) {
+ capacity *= 2;
+ tags = xrealloc(tags, capacity * sizeof(tag_t));
+ }
+
+ tags[len++] = value;
+
+ if (json_next_token(&next, stream) == false) {
+ free(tags);
+ free(value.name);
+ json_token_free(&next);
+ return false;
+ }
+
+ if (next.type == TOK_COMMA) {
+ continue;
+ } else if (next.type == TOK_RBRACE) {
+ break;
+ } else {
+ free(tags);
+ free(value.name);
+ json_token_free(&next);
+ return false;
+ }
+
+ }
+
+ data->compound.tags = xalloc(len * sizeof(tag_t));
+ data->compound.size = len;
+ memcpy(data->compound.tags, tags, len * sizeof(tag_t));
+ free(tags);
+
+ return true;
+}
+
+static bool json_read_value(tag_t *tag, const stream_t *stream, token_t *first) {
+
+ token_t token;
+
+ if (first != NULL)
+ token = *first;
+ else if (json_next_token(&token, stream) == false)
+ return false;
+
+ tag->name = "";
+ tag->name_len = 0;
+
+ bool ok = true;
+
+ switch (token.type) {
+ case TOK_RBRACK:
+ case TOK_RBRACE:
+ case TOK_COLON:
+ case TOK_COMMA:
+ case TOK_NULL:
+ ok = false;
+ break;
+ case TOK_LBRACK:
+ tag->type = TAG_LIST;
+ ok = json_read_list(&tag->data, stream);
+ break;
+ case TOK_LBRACE:
+ tag->type = TAG_COMPOUND;
+ ok = json_read_compound(&tag->data, stream);
+ break;
+ case TOK_STRING:
+ tag->type = TAG_STRING;
+ tag->data.string.data = token.data.string.data;
+ tag->data.string.size = token.data.string.len;
+ break;
+ case TOK_NUMBER:
+ tag->type = TAG_LONG;
+ tag->data.l = token.data.number;
+ break;
+ case TOK_DOUBLE:
+ tag->type = TAG_DOUBLE;
+ tag->data.d = token.data.decimal;
+ break;
+ case TOK_BOOL:
+ tag->type = TAG_BYTE;
+ tag->data.b = token.data.b ? 1 : 0;
+ break;
+ }
+
+ return ok;
+}
+
+
+bool json_read(tag_t *tag, const stream_t *stream) {
+ return json_read_value(tag, stream, NULL);
+}
diff --git a/src/main.c b/src/main.c
index fec6d06..f4419eb 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,24 +1,54 @@
-#include "tag.h"
#include "lib.h"
+#include "tag.h"
+#include "flags.h"
+
#include <stdio.h>
+#include <stdlib.h>
+
+__attribute__((__noreturn__))
+void version() {
+ fprintf(stderr, "nbtvis v0.0.1\n");
+ fprintf(stderr, "Copyright (C) 2023 Freya Murphy\n");
+ exit(0);
+}
+
+__attribute__((__noreturn__))
+void help() {
+ fprintf(stderr, "Usage: nbtvis [OPTION]... [INFILE] [OUTFILE]\n\n");
+ fprintf(stderr, "\t-j\tinput data is JSON\n");
+ fprintf(stderr, "\t-s\tinput data is SNBT\n");
+ fprintf(stderr, "\t-n\tinput data is NBT\n");
+ fprintf(stderr, "\t-J\toutput data is JSON\n");
+ fprintf(stderr, "\t-S\toutput data is SNBT\n");
+ fprintf(stderr, "\t-N\toutput data is NBT\n\n");
+ fprintf(stderr, "\t-h --help\tprint the help message\n");
+ fprintf(stderr, "\t-v --version\tprint the version\n");
+ fprintf(stderr, "\t--in=<file>\tset input file name\n");
+ fprintf(stderr, "\t--out=<file>\tset output file name\n");
+ exit(0);
+}
+
+int main(int argc, char **argv) {
-int main (int argc, char** argv) {
+ flags_t flags;
+ parse_flags(&flags, argc, argv);
- if (argc != 2) {
- printf("usage: nbtvis file.nbt\n");
- return 0;
- }
+ if (flags.help)
+ help();
- stream_t stream = stream_open(argv[1], "rb");
+ if (flags.version)
+ version();
tag_t tag;
- if (tag_read(&tag, &stream, true) == false)
- error_and_die("failed to read tag\n");
+ if (tag_read(&tag, &flags.in, flags.fin) == false)
+ error_and_die("error: failed to read tag\n");
if (tag.type != TAG_COMPOUND)
- error_and_die("root tag is not of type compound\n");
+ error_and_die("error: nbt tag not a valid compound tag\n");
- tag_print(&tag);
+ if (tag_print(&tag, &flags.out, flags.fout) == false)
+ error_and_die("error: failed to write tag\n");
+ tag_free(&tag);
- stream_close(&stream);
+ return 0;
}
diff --git a/src/nbt/nbt.h b/src/nbt/nbt.h
new file mode 100644
index 0000000..13c0606
--- /dev/null
+++ b/src/nbt/nbt.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "../tag.h"
+#include "../stream.h"
+
+bool nbt_read(tag_t *tag, const stream_t *stream);
+bool nbt_print(const tag_t *tag, const stream_t *stream);
diff --git a/src/nbt/print.c b/src/nbt/print.c
new file mode 100644
index 0000000..4eac31f
--- /dev/null
+++ b/src/nbt/print.c
@@ -0,0 +1,125 @@
+#include "nbt.h"
+
+static bool nbt_print_header(const tag_t *tag, const stream_t *stream, bool named) {
+ if (stream_write_i8(stream, tag->type) == false)
+ return false;
+ if (!named)
+ return true;
+ if (stream_write_u16(stream, tag->name_len) == false)
+ return false;
+ if (tag->name_len > 0)
+ if (stream_write(stream, tag->name, tag->name_len) == false)
+ return false;
+ return true;
+}
+
+static bool nbt_print_byte_array(const tagdata_t *data, const stream_t *stream) {
+ if (stream_write_i32(stream, data->b_arr.size) == false)
+ return false;
+ for (int32_t i = 0; i < data->b_arr.size; i++)
+ if (stream_write_i8(stream, data->b_arr.data[i]) == false)
+ return false;
+ return true;
+}
+
+static bool nbt_print_int_array(const tagdata_t *data, const stream_t *stream) {
+ if (stream_write_i32(stream, data->i_arr.size) == false)
+ return false;
+ for (int32_t i = 0; i < data->i_arr.size; i++)
+ if (stream_write_i32(stream, data->i_arr.data[i]) == false)
+ return false;
+ return true;
+}
+
+static bool nbt_print_long_array(const tagdata_t *data, const stream_t *stream) {
+ if (stream_write_i32(stream, data->l_arr.size) == false)
+ return false;
+ for (int32_t i = 0; i < data->l_arr.size; i++)
+ if (stream_write_i64(stream, data->l_arr.data[i]) == false)
+ return false;
+ return true;
+}
+
+static bool nbt_print_string(const tagdata_t *data, const stream_t *stream) {
+ if (stream_write_u16(stream, data->string.size) == false)
+ return false;
+ if (data->string.size < 1)
+ return true;
+ if (stream_write(stream, data->string.data, data->string.size) == false)
+ return false;
+ return true;
+}
+
+static bool nbt_print_data(const tag_t *tag, const stream_t *stream);
+
+static bool nbt_print_list(const tagdata_t *data, const stream_t *stream) {
+ if (stream_write_i8(stream, data->list.type) == false)
+ return false;
+ if (stream_write_i32(stream, data->list.size) == false)
+ return false;
+ for (int32_t i = 0; i < data->list.size; i++)
+ if (nbt_print_data(&data->list.tags[i], stream) == false)
+ return false;
+ return true;
+}
+
+static bool nbt_print_compound(const tagdata_t *data, const stream_t *stream) {
+ for (int32_t i = 0; i < data->compound.size; i++)
+ if (nbt_print(&data->compound.tags[i], stream) == false)
+ return false;
+ if (stream_write_i8(stream, TAG_END) == false)
+ return false;
+ return true;
+}
+
+static bool nbt_print_data(const tag_t *tag, const stream_t *stream) {
+ bool ok = true;
+
+ switch (tag->type) {
+ case TAG_END:
+ // tag end has no data
+ break;
+ case TAG_BYTE:
+ ok = stream_write_i8(stream, tag->data.b);
+ break;
+ case TAG_SHORT:
+ ok = stream_write_i16(stream, tag->data.s);
+ break;
+ case TAG_INT:
+ case TAG_FLOAT:
+ ok = stream_write_i32(stream, tag->data.i);
+ break;
+ case TAG_LONG:
+ case TAG_DOUBLE:
+ ok = stream_write_i64(stream, tag->data.l);
+ break;
+ case TAG_BYTE_ARRAY:
+ ok = nbt_print_byte_array(&tag->data, stream);
+ break;
+ case TAG_STRING:
+ ok = nbt_print_string(&tag->data, stream);
+ break;
+ case TAG_LIST:
+ ok = nbt_print_list(&tag->data, stream);
+ break;
+ case TAG_COMPOUND:
+ ok = nbt_print_compound(&tag->data, stream);
+ break;
+ case TAG_INT_ARRAY:
+ ok = nbt_print_int_array(&tag->data, stream);
+ break;
+ case TAG_LONG_ARRAY:
+ ok = nbt_print_long_array(&tag->data, stream);
+ break;
+ }
+
+ return ok;
+}
+
+bool nbt_print(const tag_t *tag, const stream_t *stream) {
+ if (nbt_print_header(tag, stream, true) == false)
+ return false;
+ if (nbt_print_data(tag, stream) == false)
+ return false;
+ return true;
+}
diff --git a/src/tag_read.c b/src/nbt/read.c
index 5f43e7c..d5dd827 100644
--- a/src/tag_read.c
+++ b/src/nbt/read.c
@@ -1,10 +1,13 @@
-#include "tag.h"
-#include "lib.h"
+#include "nbt.h"
+#include "../lib.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-bool tag_read_header(tag_t *tag, stream_t *stream, bool named) {
+static bool nbt_read_data(tag_t *tag, const stream_t *stream);
+
+static bool nbt_read_header(tag_t *tag, const stream_t *stream, bool named) {
bool ok = true;
if (stream_read_i8(stream, &tag->type) == false)
@@ -34,7 +37,7 @@ bool tag_read_header(tag_t *tag, stream_t *stream, bool named) {
}
-static bool tag_read_byte_array(tagdata_t *data, stream_t *stream) {
+static bool nbt_read_byte_array(tagdata_t *data, const stream_t *stream) {
if (stream_read_i32(stream, &data->b_arr.size) == false)
return false;
if (data->b_arr.size == 0) {
@@ -48,7 +51,7 @@ static bool tag_read_byte_array(tagdata_t *data, stream_t *stream) {
return true;
}
-static bool tag_read_int_array(tagdata_t *data, stream_t *stream) {
+static bool nbt_read_int_array(tagdata_t *data, const stream_t *stream) {
if (stream_read_i32(stream, &data->i_arr.size) == false)
return false;
if (data->i_arr.size == 0) {
@@ -62,7 +65,7 @@ static bool tag_read_int_array(tagdata_t *data, stream_t *stream) {
return true;
}
-static bool tag_read_long_array(tagdata_t *data, stream_t *stream) {
+static bool nbt_read_long_array(tagdata_t *data, const stream_t *stream) {
if (stream_read_i32(stream, &data->l_arr.size) == false)
return false;
if (data->l_arr.size == 0) {
@@ -76,7 +79,7 @@ static bool tag_read_long_array(tagdata_t *data, stream_t *stream) {
return true;
}
-static bool tag_read_string(tagdata_t *data, stream_t *stream) {
+static bool nbt_read_string(tagdata_t *data, const stream_t *stream) {
if (stream_read_u16(stream, &data->string.size) == false)
return false;
if (data->string.size < 1) {
@@ -89,7 +92,7 @@ static bool tag_read_string(tagdata_t *data, stream_t *stream) {
return true;
}
-static bool tag_read_list(tagdata_t *data, stream_t *stream) {
+static bool nbt_read_list(tagdata_t *data, const stream_t *stream) {
if (stream_read_i8(stream, &data->list.type) == false)
return false;
if (stream_read_i32(stream, &data->list.size) == false)
@@ -107,14 +110,14 @@ static bool tag_read_list(tagdata_t *data, stream_t *stream) {
tag.type = data->list.type;
tag.name = "";
tag.name_len = 0;
- if (tag_read_data(&tag, stream) == false)
+ if (nbt_read_data(&tag, stream) == false)
return false;
data->list.tags[i] = tag;
}
return true;
}
-static bool tag_read_compound(tagdata_t *data, stream_t *stream) {
+static bool nbt_read_compound(tagdata_t *data, const stream_t *stream) {
int32_t size = 0;
int32_t capacity = 8;
tag_t *tags = xalloc(capacity * sizeof(tag_t));
@@ -123,14 +126,18 @@ static bool tag_read_compound(tagdata_t *data, stream_t *stream) {
tag_t tag;
- if (tag_read_header(&tag, stream, true) == false)
+ if (nbt_read_header(&tag, stream, true) == false) {
+ free(tags);
return false;
+ }
if (tag.type == TAG_END)
break;
- if (tag_read_data(&tag, stream) == false)
+ if (nbt_read_data(&tag, stream) == false) {
+ free(tags);
return false;
+ }
if (size == capacity) {
capacity *= 2;
@@ -147,7 +154,8 @@ static bool tag_read_compound(tagdata_t *data, stream_t *stream) {
return true;
}
-bool tag_read_data(tag_t *tag, stream_t *stream) {
+
+static bool nbt_read_data(tag_t *tag, const stream_t *stream) {
bool ok = true;
switch (tag->type) {
@@ -169,33 +177,33 @@ bool tag_read_data(tag_t *tag, stream_t *stream) {
ok = stream_read_i64(stream, &tag->data.l);
break;
case TAG_BYTE_ARRAY:
- ok = tag_read_byte_array(&tag->data, stream);
+ ok = nbt_read_byte_array(&tag->data, stream);
break;
case TAG_STRING:
- ok = tag_read_string(&tag->data, stream);
+ ok = nbt_read_string(&tag->data, stream);
break;
case TAG_LIST:
- ok = tag_read_list(&tag->data, stream);
+ ok = nbt_read_list(&tag->data, stream);
break;
case TAG_COMPOUND:
- ok = tag_read_compound(&tag->data, stream);
+ ok = nbt_read_compound(&tag->data, stream);
break;
case TAG_INT_ARRAY:
- ok = tag_read_int_array(&tag->data, stream);
+ ok = nbt_read_int_array(&tag->data, stream);
break;
case TAG_LONG_ARRAY:
- ok = tag_read_long_array(&tag->data, stream);
+ ok = nbt_read_long_array(&tag->data, stream);
break;
break;
};
return ok;
}
-bool tag_read(tag_t *tag, stream_t *stream, bool named) {
+bool nbt_read(tag_t *tag, const stream_t *stream) {
memset(tag, 0, sizeof(tag_t));
- if (tag_read_header(tag, stream, named) == false)
+ if (nbt_read_header(tag, stream, true) == false)
return false;
- if (tag_read_data(tag, stream) == false)
+ if (nbt_read_data(tag, stream) == false)
return false;
return true;
}
diff --git a/src/stream.c b/src/stream.c
index 622fafa..6780d02 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -7,19 +7,37 @@
#include <stdlib.h>
#include <string.h>
-stream_t stream_open(char *path, char* mode) {
+static uint64_t longswap(uint64_t ll) {
+ if (htons(20) == 20)
+ return ll;
+
+ union { uint64_t ll; uint8_t c[8]; } out = {0};
+ union { uint64_t ll; uint8_t c[8]; } in = {ll};
+
+ for (int i = 0; i < 8; i++)
+ out.c[7-i] = in.c[i];
+
+ return out.ll;
+}
+
+
+stream_t stream_open(const char *path, const char* mode) {
stream_t stream;
if (strcmp("-", path) == 0) {
- stream.__file = stdin;
+ if (*mode == 'r')
+ stream.__file = stdin;
+ else
+ stream.__file = stdout;
stream.__alloc = false;
+ return stream;
}
stream.__file = fopen(path, mode);
stream.__alloc = true;
if (stream.__file == NULL) {
- perror_and_die("cannot read '%s'", path);
+ perror_and_die("cannot open '%s'", path);
};
return stream;
@@ -30,7 +48,7 @@ void stream_close(stream_t *stream) {
fclose(stream->__file);
}
-bool stream_read(stream_t *stream, void *res, size_t amount) {
+bool stream_read(const stream_t *stream, void *res, size_t amount) {
size_t read;
read = fread(res, 1, amount, stream->__file);
@@ -38,62 +56,89 @@ bool stream_read(stream_t *stream, void *res, size_t amount) {
if (feof(stream->__file) || errno == 0)
return false;
else
- perror_and_die("cannot read open stream");
+ perror_and_die("cannot read from stream");
}
return true;
}
-bool stream_read_i8(stream_t *stream, int8_t *res) {
+bool stream_read_i8(const stream_t *stream, int8_t *res) {
if (stream_read(stream, res, 1) == false)
return false;
return true;
}
-bool stream_read_i16(stream_t *stream, int16_t *res) {
+bool stream_read_i16(const stream_t *stream, int16_t *res) {
if (stream_read(stream, res, 2) == false)
return false;
*res = ntohs(*res);
return true;
}
-bool stream_read_i32(stream_t *stream, int32_t *res) {
+bool stream_read_i32(const stream_t *stream, int32_t *res) {
if (stream_read(stream, res, 4) == false)
return false;
*res = ntohl(*res);
return true;
}
-static uint64_t ntohll(uint64_t ll) {
- if (htons(20) == 20)
- return ll;
+bool stream_read_i64(const stream_t *stream, int64_t *res) {
+ if (stream_read(stream, res, 8) == false)
+ return false;
+ *res = longswap(*res);
+ return true;
+}
- union { uint64_t ll; uint8_t c[8]; } out = {0};
- union { uint64_t ll; uint8_t c[8]; } in = {ll};
+bool stream_read_u16(const stream_t *stream, uint16_t *res) {
+ if (stream_read(stream, res, 2) == false)
+ return false;
+ *res = ntohs(*res);
+ return true;
+}
- for (int i = 0; i < 8; i++)
- out.c[7-i] = in.c[i];
+bool stream_write(const stream_t *stream, const void *buf, size_t amount) {
+ size_t wrote;
+ wrote = fwrite(buf, 1, amount, stream->__file);
- return out.ll;
+ if (wrote == 0)
+ perror_and_die("cannot write to stream");
+
+ if (wrote < amount)
+ return false;
+
+ return true;
}
-bool stream_read_i64(stream_t *stream, int64_t *res) {
- if (stream_read(stream, res, 8) == false)
+bool stream_write_i8(const stream_t *stream, int8_t b) {
+ if (stream_write(stream, &b, 1) == false)
return false;
- *res = ntohll(*res);
return true;
}
-bool stream_read_u16(stream_t *stream, uint16_t *res) {
- if (stream_read(stream, res, 2) == false)
+bool stream_write_i16(const stream_t *stream, int16_t s) {
+ s = htons(s);
+ if (stream_write(stream, &s, 2) == false)
return false;
- *res = ntohs(*res);
return true;
}
-bool stream_read_u32(stream_t *stream, uint32_t *res) {
- if (stream_read(stream, res, 4) == false)
+bool stream_write_i32(const stream_t *stream, int32_t i) {
+ i = htonl(i);
+ if (stream_write(stream, &i, 4) == false)
+ return false;
+ return true;
+}
+
+bool stream_write_i64(const stream_t *stream, int64_t l) {
+ l = longswap(l);
+ if (stream_write(stream, &l, 8) == false)
+ return false;
+ return true;
+}
+
+bool stream_write_u16(const stream_t *stream, uint16_t s) {
+ s = htons(s);
+ if (stream_write(stream, &s, 2) == false)
return false;
- *res = ntohl(*res);
return true;
}
diff --git a/src/stream.h b/src/stream.h
index e9fd920..d9a05ce 100644
--- a/src/stream.h
+++ b/src/stream.h
@@ -9,14 +9,19 @@ typedef struct {
bool __alloc;
} stream_t;
-stream_t stream_open(char *path, char* mode);
+stream_t stream_open(const char *path, const char* mode);
void stream_close(stream_t *stream);
-bool stream_read(stream_t *stream, void *res, size_t amount);
-bool stream_read_i8(stream_t *stream, int8_t *res);
-bool stream_read_i16(stream_t *stream, int16_t *res);
-bool stream_read_i32(stream_t *stream, int32_t *res);
-bool stream_read_i64(stream_t *stream, int64_t *res);
+bool stream_read(const stream_t *stream, void *res, size_t amount);
+bool stream_read_i8(const stream_t *stream, int8_t *res);
+bool stream_read_i16(const stream_t *stream, int16_t *res);
+bool stream_read_i32(const stream_t *stream, int32_t *res);
+bool stream_read_i64(const stream_t *stream, int64_t *res);
+bool stream_read_u16(const stream_t *stream, uint16_t *res);
-bool stream_read_u16(stream_t *stream, uint16_t *res);
-bool stream_read_u32(stream_t *stream, uint32_t *res);
+bool stream_write(const stream_t *stream, const void *buf, size_t amount);
+bool stream_write_i8(const stream_t *stream, int8_t b);
+bool stream_write_i16(const stream_t *stream, int16_t s);
+bool stream_write_i32(const stream_t *stream, int32_t i);
+bool stream_write_i64(const stream_t *stream, int64_t l);
+bool stream_write_u16(const stream_t *stream, uint16_t s);
diff --git a/src/tag.c b/src/tag.c
new file mode 100644
index 0000000..25d9d32
--- /dev/null
+++ b/src/tag.c
@@ -0,0 +1,66 @@
+#include "tag.h"
+#include "nbt/nbt.h"
+#include "json/json.h"
+
+#include <stdlib.h>
+
+void tag_free(tag_t *tag) {
+ if (tag->name_len > 0 && tag->name != NULL)
+ free(tag->name);
+ switch(tag->type) {
+ case TAG_END:
+ case TAG_BYTE:
+ case TAG_SHORT:
+ case TAG_INT:
+ case TAG_LONG:
+ case TAG_FLOAT:
+ case TAG_DOUBLE:
+ break;
+ case TAG_BYTE_ARRAY:
+ free(tag->data.b_arr.data);
+ break;
+ case TAG_STRING:
+ free(tag->data.string.data);
+ break;
+ case TAG_LIST:
+ for (int32_t i = 0; i < tag->data.list.size; i++)
+ tag_free(&tag->data.list.tags[i]);
+ free(tag->data.list.tags);
+ break;
+ case TAG_COMPOUND:
+ for (int32_t i = 0; i < tag->data.compound.size; i++)
+ tag_free(&tag->data.compound.tags[i]);
+ free(tag->data.compound.tags);
+ break;
+ case TAG_INT_ARRAY:
+ free(tag->data.i_arr.data);
+ break;
+ case TAG_LONG_ARRAY:
+ free(tag->data.l_arr.data);
+ break;
+ }
+}
+
+bool tag_read(tag_t *tag, const stream_t *stream, format_t format) {
+ switch (format) {
+ case JSON:
+ return json_read(tag, stream);
+ case NBT:
+ return nbt_read(tag, stream);
+ case SNBT:
+ default:
+ return false;
+ }
+}
+
+bool tag_print(tag_t *tag, const stream_t *stream, format_t format) {
+ switch (format) {
+ case JSON:
+ return json_print(tag, stream);
+ case NBT:
+ return nbt_print(tag, stream);
+ case SNBT:
+ default:
+ return false;
+ }
+}
diff --git a/src/tag.h b/src/tag.h
index 93eff64..d0033a5 100644
--- a/src/tag.h
+++ b/src/tag.h
@@ -67,7 +67,13 @@ typedef struct tag_t {
char *name;
} tag_t;
-bool tag_read_header(tag_t *tag, stream_t *stream, bool named);
-bool tag_read_data(tag_t *tag, stream_t *stream);
-bool tag_read(tag_t *tag, stream_t *stream, bool named);
-void tag_print(const tag_t *tag);
+typedef enum {
+ JSON,
+ NBT,
+ SNBT
+} format_t;
+
+void tag_free(tag_t *tag);
+bool tag_read(tag_t *tag, const stream_t *stream, format_t format);
+bool tag_print(tag_t *tag, const stream_t *stream, format_t format);
+
diff --git a/src/tag_print.c b/src/tag_print.c
deleted file mode 100644
index b6a8e1a..0000000
--- a/src/tag_print.c
+++ /dev/null
@@ -1,101 +0,0 @@
-#include "tag.h"
-#include <stdarg.h>
-#include <stdio.h>
-
-__attribute__((format(printf, 2, 3)))
-static void printi(int depth, const char *format, ...) {
- for (int i = 0; i < depth; i++)
- printf("\t");
- va_list list;
- va_start(list, format);
- vprintf(format, list);
-}
-
-static void tag_print_impl(const tag_t *tag, int depth);
-
-static void tag_print_data(const tag_t *tag, int depth) {
- switch (tag->type) {
- case TAG_BYTE:
- printf("%hhd", tag->data.b);
- break;
- case TAG_SHORT:
- printf("%hd", tag->data.s);
- break;
- case TAG_INT:
- printf("%d", tag->data.i);
- break;
- case TAG_LONG:
- printf("%ld", tag->data.l);
- break;
- case TAG_FLOAT:
- printf("%f", tag->data.f);
- break;
- case TAG_DOUBLE:
- printf("%lf", tag->data.d);
- break;
- case TAG_BYTE_ARRAY:
- printf("[");
- for (int32_t i = 0; i < tag->data.b_arr.size; i++) {
- if (i != 0) printf(",");
- printf("%hhd", tag->data.b_arr.data[i]);
- }
- printf("]");
- break;
- case TAG_STRING:
- if (tag->data.string.size > 1)
- printf("\"%.*s\"", tag->data.string.size, tag->data.string.data);
- else
- printf("\"\"");
- break;
- case TAG_LIST:
- printf("[\n");
- for (int32_t i = 0; i < tag->data.list.size; i++) {
- if (i != 0) printf(",\n");
- tag_print_impl(&tag->data.list.tags[i], depth + 1);
- }
- printf("\n");
- printi(depth, "]");
- break;
- case TAG_COMPOUND:
- printf("{\n");
- for (int32_t i = 0; i < tag->data.compound.size; i++) {
- if (i != 0) printf(",\n");
- tag_print_impl(&tag->data.compound.tags[i], depth + 1);
- }
- printf("\n");
- printi(depth, "}");
- break;
- case TAG_INT_ARRAY:
- printi(depth, "[");
- for (int32_t i = 0; i < tag->data.i_arr.size; i++) {
- if (i != 0) printf(",");
- printf("%d", tag->data.i_arr.data[i]);
- }
- printf("]");
- break;
- case TAG_LONG_ARRAY:
- printf("[");
- for (int32_t i = 0; i < tag->data.l_arr.size; i++) {
- if (i != 0) printf(",");
- printf("%ld", tag->data.l_arr.data[i]);
- }
- printf("]");
- break;
- case TAG_END:
- break;
- }
-}
-
-static void tag_print_impl(const tag_t *tag, int depth) {
- if (tag->name_len > 0) {
- printi(depth, "\"%.*s\":\t", tag->name_len, tag->name);
- } else {
- for (int i = 0; i < depth; i++) printf("\t");
- }
- tag_print_data(tag, depth);
-}
-
-void tag_print(const tag_t *tag) {
- tag_print_impl(tag, 0);
- printf("\n");
-}