add json support and other things
This commit is contained in:
parent
4198e0a819
commit
42d1c82a0b
14 changed files with 1388 additions and 211 deletions
155
src/flags.c
Normal file
155
src/flags.c
Normal 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
17
src/flags.h
Normal 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
7
src/json/json.h
Normal 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
173
src/json/print.c
Normal 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
634
src/json/read.c
Normal 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);
|
||||
}
|
52
src/main.c
52
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) {
|
||||
|
||||
if (argc != 2) {
|
||||
printf("usage: nbtvis file.nbt\n");
|
||||
return 0;
|
||||
}
|
||||
flags_t flags;
|
||||
parse_flags(&flags, argc, argv);
|
||||
|
||||
stream_t stream = stream_open(argv[1], "rb");
|
||||
if (flags.help)
|
||||
help();
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
7
src/nbt/nbt.h
Normal file
7
src/nbt/nbt.h
Normal 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
125
src/nbt/print.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
171
src/stream.c
171
src/stream.c
|
@ -7,64 +7,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
stream_t stream_open(char *path, char* mode) {
|
||||
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) {
|
||||
static uint64_t longswap(uint64_t ll) {
|
||||
if (htons(20) == 20)
|
||||
return ll;
|
||||
|
||||
|
@ -77,23 +20,125 @@ static uint64_t ntohll(uint64_t 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;
|
||||
*res = ntohll(*res);
|
||||
else
|
||||
perror_and_die("cannot read from stream");
|
||||
}
|
||||
|
||||
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)
|
||||
return false;
|
||||
*res = ntohs(*res);
|
||||
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)
|
||||
return false;
|
||||
*res = ntohl(*res);
|
||||
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;
|
||||
}
|
||||
|
|
21
src/stream.h
21
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);
|
||||
|
|
66
src/tag.c
Normal file
66
src/tag.c
Normal 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;
|
||||
}
|
||||
}
|
14
src/tag.h
14
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);
|
||||
|
||||
|
|
101
src/tag_print.c
101
src/tag_print.c
|
@ -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");
|
||||
}
|
Loading…
Reference in a new issue