add json support and other things

This commit is contained in:
Freya Murphy 2023-12-15 23:02:45 -05:00
parent 4198e0a819
commit 42d1c82a0b
No known key found for this signature in database
GPG key ID: 988032A5638EE799
14 changed files with 1388 additions and 211 deletions

155
src/flags.c Normal file
View file

@ -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");
}
}

17
src/flags.h Normal file
View file

@ -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);

7
src/json/json.h Normal file
View file

@ -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);

173
src/json/print.c Normal file
View file

@ -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;
}

634
src/json/read.c Normal file
View file

@ -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);
}

View file

@ -1,24 +1,54 @@
#include "tag.h"
#include "lib.h" #include "lib.h"
#include "tag.h"
#include "flags.h"
#include <stdio.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) {
if (argc != 2) { flags_t flags;
printf("usage: nbtvis file.nbt\n"); parse_flags(&flags, argc, argv);
return 0;
}
stream_t stream = stream_open(argv[1], "rb"); if (flags.help)
help();
if (flags.version)
version();
tag_t tag; tag_t tag;
if (tag_read(&tag, &stream, true) == false) if (tag_read(&tag, &flags.in, flags.fin) == false)
error_and_die("failed to read tag\n"); error_and_die("error: failed to read tag\n");
if (tag.type != TAG_COMPOUND) 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;
} }

7
src/nbt/nbt.h Normal file
View file

@ -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);

125
src/nbt/print.c Normal file
View file

@ -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;
}

View file

@ -1,10 +1,13 @@
#include "tag.h" #include "nbt.h"
#include "lib.h" #include "../lib.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.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; bool ok = true;
if (stream_read_i8(stream, &tag->type) == false) 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) if (stream_read_i32(stream, &data->b_arr.size) == false)
return false; return false;
if (data->b_arr.size == 0) { if (data->b_arr.size == 0) {
@ -48,7 +51,7 @@ static bool tag_read_byte_array(tagdata_t *data, stream_t *stream) {
return true; 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) if (stream_read_i32(stream, &data->i_arr.size) == false)
return false; return false;
if (data->i_arr.size == 0) { if (data->i_arr.size == 0) {
@ -62,7 +65,7 @@ static bool tag_read_int_array(tagdata_t *data, stream_t *stream) {
return true; 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) if (stream_read_i32(stream, &data->l_arr.size) == false)
return false; return false;
if (data->l_arr.size == 0) { if (data->l_arr.size == 0) {
@ -76,7 +79,7 @@ static bool tag_read_long_array(tagdata_t *data, stream_t *stream) {
return true; 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) if (stream_read_u16(stream, &data->string.size) == false)
return false; return false;
if (data->string.size < 1) { if (data->string.size < 1) {
@ -89,7 +92,7 @@ static bool tag_read_string(tagdata_t *data, stream_t *stream) {
return true; 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) if (stream_read_i8(stream, &data->list.type) == false)
return false; return false;
if (stream_read_i32(stream, &data->list.size) == 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.type = data->list.type;
tag.name = ""; tag.name = "";
tag.name_len = 0; tag.name_len = 0;
if (tag_read_data(&tag, stream) == false) if (nbt_read_data(&tag, stream) == false)
return false; return false;
data->list.tags[i] = tag; data->list.tags[i] = tag;
} }
return true; 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 size = 0;
int32_t capacity = 8; int32_t capacity = 8;
tag_t *tags = xalloc(capacity * sizeof(tag_t)); 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; tag_t tag;
if (tag_read_header(&tag, stream, true) == false) if (nbt_read_header(&tag, stream, true) == false) {
free(tags);
return false; return false;
}
if (tag.type == TAG_END) if (tag.type == TAG_END)
break; break;
if (tag_read_data(&tag, stream) == false) if (nbt_read_data(&tag, stream) == false) {
free(tags);
return false; return false;
}
if (size == capacity) { if (size == capacity) {
capacity *= 2; capacity *= 2;
@ -147,7 +154,8 @@ static bool tag_read_compound(tagdata_t *data, stream_t *stream) {
return true; 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; bool ok = true;
switch (tag->type) { 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); ok = stream_read_i64(stream, &tag->data.l);
break; break;
case TAG_BYTE_ARRAY: case TAG_BYTE_ARRAY:
ok = tag_read_byte_array(&tag->data, stream); ok = nbt_read_byte_array(&tag->data, stream);
break; break;
case TAG_STRING: case TAG_STRING:
ok = tag_read_string(&tag->data, stream); ok = nbt_read_string(&tag->data, stream);
break; break;
case TAG_LIST: case TAG_LIST:
ok = tag_read_list(&tag->data, stream); ok = nbt_read_list(&tag->data, stream);
break; break;
case TAG_COMPOUND: case TAG_COMPOUND:
ok = tag_read_compound(&tag->data, stream); ok = nbt_read_compound(&tag->data, stream);
break; break;
case TAG_INT_ARRAY: case TAG_INT_ARRAY:
ok = tag_read_int_array(&tag->data, stream); ok = nbt_read_int_array(&tag->data, stream);
break; break;
case TAG_LONG_ARRAY: case TAG_LONG_ARRAY:
ok = tag_read_long_array(&tag->data, stream); ok = nbt_read_long_array(&tag->data, stream);
break; break;
break; break;
}; };
return ok; 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)); memset(tag, 0, sizeof(tag_t));
if (tag_read_header(tag, stream, named) == false) if (nbt_read_header(tag, stream, true) == false)
return false; return false;
if (tag_read_data(tag, stream) == false) if (nbt_read_data(tag, stream) == false)
return false; return false;
return true; return true;
} }

View file

@ -7,64 +7,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
stream_t stream_open(char *path, char* mode) { static uint64_t longswap(uint64_t ll) {
stream_t stream;
if (strcmp("-", path) == 0) {
stream.__file = stdin;
stream.__alloc = false;
}
stream.__file = fopen(path, mode);
stream.__alloc = true;
if (stream.__file == NULL) {
perror_and_die("cannot read '%s'", path);
};
return stream;
}
void stream_close(stream_t *stream) {
if (stream->__alloc)
fclose(stream->__file);
}
bool stream_read(stream_t *stream, void *res, size_t amount) {
size_t read;
read = fread(res, 1, amount, stream->__file);
if (read == 0) {
if (feof(stream->__file) || errno == 0)
return false;
else
perror_and_die("cannot read open stream");
}
return true;
}
bool stream_read_i8(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) {
if (stream_read(stream, res, 2) == false)
return false;
*res = ntohs(*res);
return true;
}
bool stream_read_i32(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) if (htons(20) == 20)
return ll; return ll;
@ -77,23 +20,125 @@ static uint64_t ntohll(uint64_t ll) {
return out.ll; return out.ll;
} }
bool stream_read_i64(stream_t *stream, int64_t *res) {
if (stream_read(stream, res, 8) == false) stream_t stream_open(const char *path, const char* mode) {
stream_t stream;
if (strcmp("-", path) == 0) {
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 open '%s'", path);
};
return stream;
}
void stream_close(stream_t *stream) {
if (stream->__alloc)
fclose(stream->__file);
}
bool stream_read(const stream_t *stream, void *res, size_t amount) {
size_t read;
read = fread(res, 1, amount, stream->__file);
if (read == 0) {
if (feof(stream->__file) || errno == 0)
return false; return false;
*res = ntohll(*res); else
perror_and_die("cannot read from stream");
}
return true; return true;
} }
bool stream_read_u16(stream_t *stream, uint16_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(const stream_t *stream, int16_t *res) {
if (stream_read(stream, res, 2) == false) if (stream_read(stream, res, 2) == false)
return false; return false;
*res = ntohs(*res); *res = ntohs(*res);
return true; return true;
} }
bool stream_read_u32(stream_t *stream, uint32_t *res) { bool stream_read_i32(const stream_t *stream, int32_t *res) {
if (stream_read(stream, res, 4) == false) if (stream_read(stream, res, 4) == false)
return false; return false;
*res = ntohl(*res); *res = ntohl(*res);
return true; return true;
} }
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;
}
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;
}
bool stream_write(const stream_t *stream, const void *buf, size_t amount) {
size_t wrote;
wrote = fwrite(buf, 1, amount, stream->__file);
if (wrote == 0)
perror_and_die("cannot write to stream");
if (wrote < amount)
return false;
return true;
}
bool stream_write_i8(const stream_t *stream, int8_t b) {
if (stream_write(stream, &b, 1) == false)
return false;
return true;
}
bool stream_write_i16(const stream_t *stream, int16_t s) {
s = htons(s);
if (stream_write(stream, &s, 2) == false)
return false;
return true;
}
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;
return true;
}

View file

@ -9,14 +9,19 @@ typedef struct {
bool __alloc; bool __alloc;
} stream_t; } 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); 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(const stream_t *stream, void *res, size_t amount);
bool stream_read_i16(stream_t *stream, int16_t *res); bool stream_read_i8(const stream_t *stream, int8_t *res);
bool stream_read_i32(stream_t *stream, int32_t *res); bool stream_read_i16(const stream_t *stream, int16_t *res);
bool stream_read_i64(stream_t *stream, int64_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_write(const stream_t *stream, const void *buf, size_t amount);
bool stream_read_u32(stream_t *stream, uint32_t *res); 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);

66
src/tag.c Normal file
View file

@ -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;
}
}

View file

@ -67,7 +67,13 @@ typedef struct tag_t {
char *name; char *name;
} tag_t; } tag_t;
bool tag_read_header(tag_t *tag, stream_t *stream, bool named); typedef enum {
bool tag_read_data(tag_t *tag, stream_t *stream); JSON,
bool tag_read(tag_t *tag, stream_t *stream, bool named); NBT,
void tag_print(const tag_t *tag); 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);

View file

@ -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");
}