nbtvis/nbt/nbt/read.c
2023-12-17 11:10:04 -05:00

207 lines
4.7 KiB
C

#include "nbt.h"
#include "lib.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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)
return false;
if (tag->type == TAG_END)
named = false;
if (named) {
if (stream_read_u16(stream, &tag->name_len) == false)
return false;
} else {
tag->name_len = 0;
}
if (tag->name_len < 1) {
tag->name = "";
} else {
tag->name = xalloc(tag->name_len);
ok = stream_read(stream, tag->name, tag->name_len);
}
if (!ok)
return false;
return true;
}
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) {
data->b_arr.data = NULL;
return true;
}
data->b_arr.data = xalloc(data->b_arr.size * sizeof(int8_t));
for (int32_t i = 0; i < data->b_arr.size; i++)
if (stream_read_i8(stream, &data->b_arr.data[i]) == false)
return false;
return true;
}
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) {
data->i_arr.data = NULL;
return true;
}
data->i_arr.data = xalloc(data->i_arr.size * sizeof(int32_t));
for (int32_t i = 0; i < data->i_arr.size; i++)
if (stream_read_i32(stream, &data->i_arr.data[i]) == false)
return false;
return true;
}
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) {
data->l_arr.data = NULL;
return true;
}
data->l_arr.data = xalloc(data->l_arr.size * sizeof(int64_t));
for (int32_t i = 0; i < data->l_arr.size; i++)
if (stream_read_i64(stream, &data->l_arr.data[i]) == false)
return false;
return true;
}
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) {
data->string.data = NULL;
return true;
}
data->string.data = xalloc(data->string.size);
if (stream_read(stream, data->string.data, data->string.size) == false)
return false;
return true;
}
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)
return false;
if (data->list.size <= 0) {
data->list.tags = NULL;
return true;
} else if (data->list.type == 0) {
// tag end is not allowed to be used with non empty list
return false;
}
data->list.tags = xalloc(data->list.size * sizeof(tag_t));
for (int32_t i = 0; i < data->list.size; i++) {
tag_t tag;
tag.type = data->list.type;
tag.name = "";
tag.name_len = 0;
if (nbt_read_data(&tag, stream) == false)
return false;
data->list.tags[i] = tag;
}
return true;
}
static bool nbt_read_compound(tagdata_t *data, const stream_t *stream) {
map_t map;
map_init(&map);
while (1) {
tag_t tag;
if (nbt_read_header(&tag, stream, true) == false) {
map_free(&map);
return false;
}
if (tag.type == TAG_END)
break;
if (tag.name_len < 1) {
map_free(&map);
return false;
}
if (nbt_read_data(&tag, stream) == false) {
map_free(&map);
return false;
}
map_put(&map, &tag);
}
data->compound = map;
return true;
}
static bool nbt_read_data(tag_t *tag, const stream_t *stream) {
bool ok = true;
switch (tag->type) {
case TAG_END:
// tag has no data
break;
case TAG_BYTE:
ok = stream_read_i8(stream, &tag->data.b);
break;
case TAG_SHORT:
ok = stream_read_i16(stream, &tag->data.s);
break;
case TAG_FLOAT:
case TAG_INT:
ok = stream_read_i32(stream, &tag->data.i);
break;
case TAG_DOUBLE:
case TAG_LONG:
ok = stream_read_i64(stream, &tag->data.l);
break;
case TAG_BYTE_ARRAY:
ok = nbt_read_byte_array(&tag->data, stream);
break;
case TAG_STRING:
ok = nbt_read_string(&tag->data, stream);
break;
case TAG_LIST:
ok = nbt_read_list(&tag->data, stream);
break;
case TAG_COMPOUND:
ok = nbt_read_compound(&tag->data, stream);
break;
case TAG_INT_ARRAY:
ok = nbt_read_int_array(&tag->data, stream);
break;
case TAG_LONG_ARRAY:
ok = nbt_read_long_array(&tag->data, stream);
break;
break;
};
return ok;
}
bool nbt_read(tag_t *tag, const stream_t *stream) {
memset(tag, 0, sizeof(tag_t));
if (nbt_read_header(tag, stream, true) == false)
return false;
if (nbt_read_data(tag, stream) == false)
return false;
return true;
}